技术开发 频道

Java 8新特性:关于默认方法的详细介绍

  但是如果我们想从接口A中调用默认实现方法foo(),而不是实现我们自己的方法,该怎么办呢?这是有可能的,引用A中的foo(),如下所示:

public class Clazz implements A, B {
    
public void foo(){
       A.
super.foo();
    }
}

  现在我不能十分确信我喜欢这个最终方案。也许它比在签名里声明默认方法的实现更为简练,正如在默认方法规范的第一手稿里所声明的:

public class Clazz implements A, B {
    
public void foo() default A.foo;
}

  但是这确实更改了语法,难道不是吗?它看起来更像一个接口的方法声明而不是实现。假若接口A和接口B定义了许多相互冲突的默认方法,而我愿意使用所有接口A的默认方法解决冲突,那又如何呢?目前我不得不一个接着一个的解决冲突,改写每一对冲突的方法。这可能需要大量的工作和书写大量的模板代码。

  我估计解决冲突的方法需要进行大量的讨论,不过看起来创建者决定接受这无法避免的灾难。

  真实的例子

  默认方法实现的真实例子可以在 JDK8早期打的包中找到。回到集合的forEach方法的例子中, 我们可以发现在java.lang.Iterable接口中,它的默认实现如下:

@FunctionalInterface
public interface Iterable<T> {
    Iterator
<T> iterator();

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

  forEach使用了一个java.util.function.Consumer功能接口类型的参数,它使得我们可以传入一个lambda表达式或者一个方法引用,如下:

List<?> list =
list.forEach(System.out::println);

  方法调用

  让我们看一下实际上是如何调用默认的方法的。如果你不熟悉这个问题,那么你可能有兴趣阅读一下Rebel实验室有关Java字节的报告。

  从客户端代码的视角来看,默认的方法仅仅是常见的虚拟方法。因此名字应该是虚拟扩展方法。因此对于把默认方法实现为接口的简单例子类来说,客户端代码将在调用默认方法的地方自动调用接口。

A clazz = new Clazz();
clazz.foo();
// invokeinterface foo()

Clazz clazz
= new Clazz();
clazz.foo();
// invokevirtual foo()
0
相关文章