Java8 中的默认方法
在之前的文章中我们已经学习了Lambda 表达式和函数式接口,现在我们讨论另一个问题,默认 方法,这对java 开发者是 革命性的,因为在Java7 之前的规定中,接口中是不能有实现方法的,但是在Java8之中,引用了默认方法,意思就是,接口中的函数可以有自己默认方法,如果子类不重写这个默认方法,那么子类可以直接使用这个默认方法,好的,下面让我们好好理解这个默认方法。
默认方法的好处是你可以向库的接口中添加新功能,并确保与这些接口的旧版本进行兼容。
1. 在 java8 中默认的方法是啥?
java8 中的默认方法只是默认方法,如果不重写它们,则它们是调用方将要调用的方法,他们是在接口中进行定义的,
例子:
public interface Moveable {
default void move(){
System.out.println("I am moving");
}
}
Moveable 接口中已经定义了一个move方法,并且提供了一个默认实现,如果任何类实现了这个接口,那么它可以不实现自己的move 方法,可以直接调用 instance.move () 方法
public class Animal implements Moveable{
public static void main(String[] args){
Animal tiger = new Animal();
tiger.move();
}
}
Output: I am moving
当然如果愿意定制的话,它可以提供自己的定制实现并重写方法,现在将调用它自己的定义的方法。
public class Animal implements Moveable{
public void move(){
System.out.println("I am running");
}
public static void main(String[] args){
Animal tiger = new Animal();
tiger.move();
}
}
Output: I am running
还有以下的好处
- 静态默认方法:可以在接口中定义静态默认方法,对该方法实现此接口的类所有实例都可用,你可以将特定接口的静态方法保存到一个接口中,而不是保存到单独的类中,这可以使你能够在类之外定义方法,并且与所有子类共享
- 它们为您提供了一种非常理想的能力,可以在不触及类的代码的情况下向类的数量添加功能,只需要在接口中添加一个默认方法,他们都可以实现,
2. 为什么 Java8 需要有默认方法
当你 作为面试官的时候,你可以问问面试者这个问题,最简单的解决方法在Java中启用lambda 表达式功能,Lambda 表达式本质上是一中函数接口类型,为了无缝的支持lambda 表达式,必须修改所有核心类,但是这些类 例如 java.util .List 不仅在JDK 类中进行了实现,同时也在成千上万的客户端代码中进行了实现,如果不兼容他们,那么这新的版本必定会不被接受
下面时添加到 java.lang 中的一个方法
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
在 java 8 之前,如果我们对java 集合进行迭代,那么你获得一个迭代器实例并调用它的next() 方法,直到 hasNext() 返回false,这是我们之前的代码,并且使用很多次,用法永远时相同的,因此,我们可以是使它变的紧凑,以便只要修改一行代码,仍然为我们工作,默认函数就可以为我们做到这一点,
现在如果要对列表中的 每个项目进行迭代和执行一些操作,您需要这样做
public class Animal implements Moveable{
public static void main(String[] args){
List<Animal> list = new ArrayList();
list.add(new Animal());
list.add(new Animal());
list.add(new Animal());
//Iterator code reduced to one line
list.forEach((Moveable p) -> p.move());
}
}
因此,这里向List 添加一个额外的方法,而没有破坏他的任何实现。
3. 在调用缺省方法的时候如何解决冲突
在 java 中,一个类可以实现n个 接口,此外,接口还是可以扩展接口,如果有默认方法在这两个接口中声明,这两个接口由单个类实现,那么很明显,类会不知道调用那个方法。
解决冲突的规则:
-
大多数首选的是类中的重写方法,在匹配任何东西之前,我们将被匹配并被调用,如果找到的话,
-
在 最具体的缺省提供接口中 选择 具有相同签名的方法,这意味着如果Animal 实现了两个接口,Moveable 和 Walkable , 那么 Walkable 扩展了 Movable。 然后Walkable 是这里最具体的接口和默认的方法将从这里选择方法签名匹配,
-
如果 Moveable 和Walkable 是独立的接口,那么就会从发生严重的冲突情况,编译器同样会不识别是哪个方法而报错,在这个时候你必须提供额外的 信息来帮助编译器从哪个接口调用默认的方法.
Walkable.super.move(); //or Moveable.super.move();具体代码
public interface Walkable {
default void move(){
System.out.println(" walkwable 接口 ");
}
}
public interface Moveable {
default void move(){
System.out.println("moveable 接口");
}
}
public class Animal implements Moveable,Walkable {
@Override
public void move() {
// 确定调用哪个接口的方法
Moveable.super.move();
}
}