Java默认方法教程

在上一篇文章中,我们了解了Lambda表达式和函数接口。现在,让我们继续讨论,并讨论另一个相关功能,即默认方法。好吧,这对于Java开发人员来说确实是革命性的。到Java 7为止,我们已经学到了很多关于接口的知识,而在编写代码或设计应用程序时,所有这些事情都已经牢记在心。在引入默认方法之后,其中一些概念将从Java 8发生巨大变化。

我将在这篇文章中讨论以下几点:

Java 8中的默认方法是什么?
为什么Java 8需要默认方法?
调用默认方法时如何解决冲突?

Java 8中的默认方法是什么?

默认方法使您可以向库的接口添加新功能,并确保与为这些接口的较早版本编写的代码二进制兼容。

顾名思义,java 8中的默认方法就是默认的。如果不重写它们,则它们是调用者类将调用的方法。它们在接口中定义。

让我们看一个例子:

public interface Moveable {
    default void move(){
        System.out.println("I am moving");
    }
}

可移动接口定义方法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

这还没有全部完成。最好的部分是以下好处:

  1. 静态默认方法:您可以在接口中定义静态默认方法,这些方法将对实现该接口的所有类实例可用。这使您可以更轻松地在库中组织帮助程序方法。您可以将特定于接口的静态方法保留在同一接口中,而不是在单独的类中。这使您可以在类之外定义方法,但仍可与所有子类共享。
  2. 它们为您提供了一种非常理想的功能,可以在不触及其代码的情况下为多个类添加功能。只需在它们都实现的接口中添加默认方法即可。

为什么Java 8需要默认方法?

这是下一个面试问题的很好的候选人。最简单的答案是在Java中启用lambda表达式的功能。Lambda表达式本质上是功能接口的类型。为了无缝支持lambda表达式,必须修改所有核心类。但是,这些核心类(例如java.util.List)不仅在JDK类中实现,而且在数千个客户端代码中也实现。核心类中任何不兼容的更改都将肯定会产生反作用,并且根本不会被接受。

默认方法打破了这种僵局,并允许在核心类中添加对功能接口的支持。让我们来看一个例子。以下是已添加到java.lang.Iterable的方法。

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

在Java 8之前,如果必须迭代Java集合,则将获得一个迭代器实例,并调用它的下一个方法,直到hasNext()返回false。这是常见的代码,我们在日常编程中已经使用了数千次。语法也总是相同的。因此,我们可以使其紧凑吗,使其仅占用一行代码,仍然像以前一样为我们完成工作。上面的功能可以做到这一点。

现在要迭代并对列表中的每个项目执行一些简单的操作,您需要做的是:

import java.util.ArrayList;
import java.util.List;
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中。长期以来,它一直是Java中非常需要的功能。现在就在我们身边。

调用默认方法时如何解决冲突?

到目前为止,一切都很好。我们的所有基础知识都很好。现在转到复杂的事情。在Java中,一个类可以实现N个接口。另外,一个接口也可以扩展另一个接口。如果在两个这样的接口中声明了默认方法,则该接口由单个类实现。那么很明显,类会混淆调用哪个方法。

解决冲突的规则如下:

1)最优选的是类中的重写方法。如果在匹配任何内容之前找到它们,它们将被匹配并被调用。
2)选择“最特定的默认提供接口”中具有相同签名的方法。这意味着如果Animal类实现了两个接口,即Moveable和Walkable,从而Walkable扩展了Moveable。那么Walkable是最具体的接口,如果方法签名匹配,则将从此处选择默认方法。
3)如果Moveable和Walkable是独立的接口,则会发生严重的冲突情况,并且编译器将抱怨然后无法确定。您必须通过提供额外的信息来帮助编译器,该信息应从哪个接口调用默认方法。例如

Walkable.super.move();
//or
Moveable.super.move();

这就是这里的所有主题。下次,当我想到一些有趣的事情时,我将继续讨论。不要忘记发表您的评论/想法或问题。


saigon has written 1445 articles

Leave a Reply