Java8 特性—— default
来源:互联网 发布:tv破解版软件 编辑:程序博客网 时间:2024/06/01 10:24
为什么要有Default方法
在 Java8 发布之际,有件事情就显得非常重要,即在不破坏 Java 现有实现架构的情况下能往接口里增加新方法。引入 Default 方法到 Java8,正是为了这个目的:优化接口的同时,避免跟现有实现架构的兼容问题。看下面例子:
List<?> list = ...list.forEach(...);// Lambda code goes here
上面的 foreach 方法既没有在 java.util.List 中声明,也没有在 java.util.Collection 中声明。(如果要使上面代码生效)容易想到的方案是在现有的接口中新增 foreach 方法,并在 JDK 中必要的地方实现 foreach。然而,一经发布,要想在某个接口中增加方法,而不修改现该接口现有的实现类,这是不可能做到的。
这样,即使我们把 Lambda 表达式引入到 Java8 中,但是因为不能牺牲向后兼容,而不可以把 Lambda 表达式和标准集合类库结合使用的话,会真的让人很泄气。
为了解决上述问题,引入了一个新的概念,即虚拟扩展方法(Virtual extension methods),通常也称之为 defender 方法,它目前可以添加到接口中,为声明的方法提供默认的实现。
简单地说,Java 接口现在可以有非抽象方法了。Default 方法带来的好处是,往接口新增一个 Default 方法,而不破坏现有的实现架构。
尽管如此,Default 方法不适合过多使用,但是对于 Java 集合 API 的优化升级,并达到无缝地结合 Lambda 表达式来说,Default 方法是至关重要的特性。
Default 方法入门
让我们从最简单可行的例子开始:下面代码定义了接口 A,以及实现了接口 A 的 Clazz 类:
public interface A { default void foo(){ System.out.println("Calling A.foo()"); }} public class Clazz implements A {}
即使 Clazz 中没有实现 foo(),代码也能编译通过。foo() 的默认实现在A接口中给出。
上例的客户端代码如下:
Clazz clazz= new Clazz();clazz.foo();// Calling A.foo()
当大家第一次听说 default 方法,通常会问:“如果一个类实现了两个接口,而这两个接口中各自定义了一个同名的 default 方法,会怎么样?”
让我们用上一个例子来解释上述情况,代码如下所示:
public interface A { default void foo(){ System.out.println("Calling A.foo()"); }} public interface B { default void foo(){ System.out.println("Calling B.foo()"); }} publicclassClazzimplements A, B {}
上面代码编译失败,报错如下:
java: class Clazz inherits unrelated defaults for foo() from types A and B
为了修复错误,在 Clazz 中,我们手工地覆写掉冲突的方法来处理这个问题,如下所示:
public class Clazz implements A, B { public void foo(){}}
可是,如果我们就想调用接口 A 中默认实现的方法 foo(),而不用自己实现的,该怎么办?那就要像下面这样使用 A#foo() 才行:
public class Clazz implements A, B { public void foo(){ A.super.foo(); }}
Default 方法的实例可以在 JDK8 中找到。回到之前集合 API 的 forEach 的例子,我们可以在接口 java.lang.Iterable 中找到 forEach 的默认实现:
@FunctionalInterface public interface Iterable{ Iterator iterator(); default void forEach(Consumer<?super T> action){ Objects.requireNonNull(action); for(T t: this){ action.accept(t); } }}
上面的 forEach 方法使用函数接口 java.util.function.Consumer 作为参数,该参数使我们能传递一个 Lambda 表达式或者方法引用到 forEach 中,如下所示:
List<?> list = ...list.forEach(System.out::println);
方法调用
我们来看看实际上是如何调用 default 方法的。
从客户端代码角度来看,default 方法只不过都是初始化的抽象方法。因此 default 方法也叫-虚拟扩展方法。如果出现上例中的那个类,该类实现了带 default 方法的接口,那么调用 default 方法的客户端代码会在调用端生成 invokeinterface 。如下所示
A clazz= new Clazz();clazz.foo();// invokeinterface foo()Clazz clazz= new Clazz();clazz.foo();// invokevirtual foo()
以防出现上述的 default 方法冲突问题,所以我们重写那个 default 方法,并代理两个接口之中某一个的 default 方法的调用,具体代码请见下面。invokespecial 意思是指 我们将调用专门的实现逻辑。
public class Clazz implements A, B { public void foo(){ A.super.foo();// invokespecial foo() }}
下面是 javap 的输出。
public void foo(); Code: 0: aload_0 1: invokespecial #2 // InterfaceMethodA.foo:()V 4: return
如上面所示的,invokespecial 指令用来调用接口中定义的方法 foo()。这从字节码的角度看也是新东西,因为先前你进行方法调用,仅是通过指向父类的 super 而不是指向父接口的。
总结
Default 方法加入到 Java 中,这是引人关注的事情。Default 方法可以认为是 Lambda 表达式和 JDK 类库之间的桥梁。引入 Default 方法的主要目的是为了升级标准 JDK 接口,另外也是为了我们最终能在 Java8 中顺畅使用 Lambda 表达式。
- Java8 特性—— default
- Java8 新特性 接口中的default方法
- Java8新特性——Lambda表达式
- Java8新特性——lambda表达式
- Java8新特性——时间API
- Java8新特性——LocalDate
- Java8新特性——Lambda
- Java8新特性——Stream
- Java8 特性—— Lambda 表达式
- Java8新特性——StreamAPI(一)
- Java8新特性——StreamAPI(二)
- Java8新特性——Optional
- Java8新特性——StreamAPI(一)
- JAVA8新特性——方法引用
- JAVA8新特性——方法引用
- Java8新特性——StreamAPI(一)
- Java8新特性——StreamAPI(二)
- Java8新特性探究—语法篇
- JQuery选择器
- 软件开发工程师(JAVA)笔试题A
- 1047. 编程团体赛(20)-PAT乙级真题
- 优化Javascript数据遍历(未测试)
- 数组中第K大的数
- Java8 特性—— default
- 使用semaphore实现生产者-消费者简单模型
- 签到题
- 1046. 划拳(15)-PAT乙级真题
- 几何知识-求任意多边形的面积。
- git
- 实现HTML编码和解码的JavaScript工具类(未测试)
- mysql 获取上个月,这个月的第一天或最后一天
- 匿名对象