Java8新特性(五) default与默认方法

导航

default

默认方法

默认方法与冲突

类与接口方法冲突

父接口与子接口方法冲突

接口与接口方法冲突


default

default是Java8新增的一个关键字,该关键字用于在接口中声明方法,使用default声明的方法称为默认方法

 

默认方法

在Java8之前接口中声明的方法全部都是抽象方法,不可以有具体实现,实现接口的类需要重写接口中声明的每个方法。但是从Java8开始接口中声明的方法可以有具体实现,更准确的说是接口中的默认方法可以有具体实现。不仅如此,从Java8开始接口中可以定义静态方法

那么为什么会出现默认方法呢?其主要原因是Java8中引入了Stream

对集合而言,每种集合都需要有获取Stream对象的方法——stream()。按照设计理念stream()方法应该在集合根接口Collection中声明,然后让Collection的实现类重写该方法。然而集合体系很庞大,如果直接在Collection接口中声明stream()方法,那么所有的Collection实现类都要重写该方法,对现有的架构改动极大。

所以如果Collection接口中可以定义有具体实现的stream()方法就不会破坏现有的架构,Collection的实现类只需要直接继承过来就可以了。在JDK8的源码中,我们可以看到Collection接口中定义有默认方法stream():

public interface Collection<E> extends Iterable<E> {
    default Stream<E> stream() { ... }
    ...
}

下面演示默认方法的使用:

public class TestDefault {
	
	public static void main(String[] ar) {
		// 用Lambda表达式表示函数式接口的一个具体实现
		MyInterface myInterface = () -> 1;
		MyInterface.getInfo(myInterface.size(), myInterface.isEmpty());
	}
	
}
// 函数式接口
interface MyInterface {
	// 抽象方法
	int size();
	// 默认犯法
	default boolean isEmpty () {
		return size() == 0;
	}
	// 静态方法
	static void getInfo(int size, boolean isEmpty) {
		System.out.println("size is " + size + ", empty: " + isEmpty);
	}
	
}

打印结果如下:

size is 1, empty: false

MyInterface接口中存在三个方法:一个抽象方法size(),一个静态方法getInfo()以及一个默认方法isEmpty()。因此MyInterface是一个函数式接口,我们可以用Lambda表达式可以表示函数式接口的一个具体实现——myInterface。

myInterface从MyInterface接口继承了有具体实现的isEmpty()方法,可以不用重写isEmpty()方法就直接调用该方法。

 

默认方法与冲突

在Java中有一条亘久不变的定理:类只能单继承,接口可以多实现。

Java8之前由于接口中的方法全部都是抽象方法,一个类如果同时实现两个拥有相同方法签名的接口并不会出现冲突,实现类只需要重写该方法即可。但是在Java8中出现了默认方法,这样一来就有可能会出现Java一直在避免的多继承问题——一个类从多个地方(类或接口)继承了有相同方法签名的方法。

遇到这种情况时,我们需要遵循下面三条原则解决冲突:

  • 一、类中方法优先级最高。类或父类中定义方法的优先级高于任何声明为默认方法的优先级。
  • 二、第一条原则无法进行判断,子接口中声明的默认方法的优先级仅次于类中声明方法的优先级。
  • 三、上述两条原则仍然无法判断,实现类必须显式重写方法或选择使用哪一个默认方法的实现。

类与接口方法冲突

public class TestDefault1 {
	
	public static void main(String[] ar) {
		new Class1().print();;
	}
	
}

class ClassFu {
	public void print() {
		System.out.println("It's ClassFu.");
	}
}

interface A1 {
	default void print () {
		System.out.println("It's A1.");
	}
}

interface B1 {
	default void print() {
		System.out.println("It's B1.");
	}
}

class Class1 extends ClassFu implements A1,B1{ }

打印结果如下:

It's ClassFu.

例子中Class1继承了ClassFu并实现了接口A1和B1,A1和B1两个接口都声明了默认方法print(),ClassFu类中也定义有print()方法。那么调用Class1对象的print()方法时应当按照第一条原则:类或父类中声明方法的优先级最高,打印结果符合预期。

父接口与子接口方法冲突

public class TestDefault2 {
	
	public static void main(String[] ar) {
		new Class2().print();
	}
	
}

interface A2 {
	default void print () {
		System.out.println("It's A2.");
	}
}

interface B2 extends A2{
	@Override
	default void print() {
		System.out.println("It's B2.");
	}
}

class Class2 implements B2,A2 {}

打印结果如下:

It's B2.

例子中Class2同时实现了接口A2和B2,且接口B2继承了A2并重写了A2中的默认方法print()。那么调用Class2对象的print()方法时应该按照第二条原则:子接口的默认方法优先级更高,打印结果符合我们的预期。

接口与接口方法冲突

public class TestDefault3 {
	
	public static void main(String[] ar) {
		new Class3().print();
	}
	
}

interface A3 {
	default void print () {
		System.out.println("It's A3.");
	}
}

interface B3 {
	default void print() {
		System.out.println("It's B3.");
	}
}

class Class3 implements A3,B3 {
	@Override
	public void print() {
		A3.super.print();
	}
}

打印结果如下:

It's A3.

例子中Class3同时实现了接口A3和B3,A3和B3接口声明了有相同方法签名的默认方法print(),此时按照第三条原则:必须在类中重写默认方法。这里我们选择A3接口中的默认方法作为方法实现,调用Class3对象的print()方法的打印结果符合预期。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章