Java8接口中的默认方法
Java8新增特性,可以为接口中添加默认方法,实现这个接口的所有类都会继承这个方法,这样看起来,接口和类的界限就有点不明显了,同时也会带来多继承,菱形问题。这样设计的初衷是什么?
重所周知,java8开始支持lambda表达式,可以把函数当做参数传递,最明显的lambda表达式应用场景莫过于对collection的每一个元素应用lambda。如果想为Collection实现lambda表达式:list.forEach(…); // 这就是lambda代码
首先想到的是为Collection的父接口iterator添加抽象方法forEach()。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。如果添加了,那么所有的iterator()实现类都要重写这个方法,如果只是jre自己的类库还好说,大量的第三方类库都使用到了java的优秀集合框架,如果都要重写,这是不合理的。
因此,如果在Java 8里使用lambda的时候,因为向前兼容的原因而不能用于collection库,那有多糟糕啊。
由于上述原因,引入了一个新的概念。虚拟扩展方法,也即通常说的defender方法, 现在可以将其加入到接口,这样可以提供声明的行为的默认实现。
简单的说,Java的接口现在可以实现方法了。默认方法带来的好处是可以为接口添加新的默认方法,而不会破坏接口的实现。
之前:Java接口纯粹是契约的集合,是一种程序设计的表达方式。从数据抽象的角度看,能够在不定义class的同时又可以定义type,将是程序设计中强大而有用的机制。Java接口就是这些纯粹的接口组成的数据抽象。Java接口只能够拥有抽象方法,它不涉及任何实现,也不能创建其对象(这一点和抽象类一致)。
多重继承模型导致额外的复杂性,其中最著名的是钻石问题或者叫“讨嫌的菱形派生”(Dreadful Diamond onDerivation、DDD)。为什么Java接口能够避免多继承的复杂性,关键在于它仅仅包含abstract方法。然而从设计的角度看,Java接口放弃了多继承的内在/固有目标,而显得是一个权宜之计。
现在:Java8之前,接口不能升级。因为在接口中添加一个方法,会导致老版本接口的所有实现类的中断。λ表达式作为核心出现,为了配合λ表达式,JDK中Collection库需要添加新的方法,如forEach(),stream()等,于是引入了默认方法(defender methods,Virtual extension methods)。它是库/框架设计的程序员的后悔药。对于以前的遗留代码,大家都不知道有这个新方法,既不会调用,也不会去实现,如同不存在;编写新代码的程序员可以将它视为保底的方法体。类型层次中任何符合override规则的方法,优先于默认方法,因为遗留代码可能正好有同样的方法存在。
默认方法,理论上抹杀了Java接口与抽象类的本质区别——前者是契约的集合,后者是接口与实现的结合体。当然,语法上两者的差别和以前一样。这就需要程序员来自觉维护两者的本质区别,把默认方法作为库、框架向前兼容的手段。
默认方法的一个好处:多继承的著名的是钻石问题(The Diamond Problem )再次需要关注。因而使以前某些人认为的“为了解决多继承问题而引入接口机制”的说法变成明显的错误——以前也是错误的认识。
默认方法的声明很简单,直接在接口中把方法声明为default,之后再写方法的实现即可。这样所有的实现类都会继承这个方法,问题是他带来的多继承问题如何解决?
Iterable中的默认方法:
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
下面是一个简单的默认方法实现:
public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}
public class Clazz implements A {
}
Clazz clazz = new Clazz();
clazz.foo(); // 调用A.foo()
下面是一个多继承:
public interface A {
default void foo(){
System.out.println("Calling A.foo()");
}
}
public interface B {
default void foo(){
System.out.println("Calling B.foo()");
}
}
public class Clazz implements A, B {
}
这段代码不能编译 有以下原因:
java:class Clazz 从types A到B给foo()继承了不相关的默认值
为了修复这个,在Clazz里我们不得不手动解决通过重写冲突的方法,或者调用某个接口中的方法:
public class Clazz implements A, B {
public void foo(){}
}
public class Clazz implements A, B {
public void foo(){
A.super.foo();
}
}
钻石问题
情况一:接口IA有子接口IB1、IB2,而类C implements IB1,IB2
package java8;
public class C implements IB1, IB2{
public static void main(String[] a) {
new C().m();
}
}
(1)如果仅仅A定义默认方法m(),执行IA的默认方法;
(2)如果只有IB1、IB2其中一个override IA定义的m(),调用“最具体接口”默认方法;
(3)如果IB1、IB2都override IA定义的m(),而C不提供自己的方法体,则编译错误!因为 “最具体接口”默认方法有两个。此时,C若Override m(),可以消去编译错误。
(4)类C提供自己的方法体时,可以提供自己的代码,也可以指定调用C implements 的直接父接口的默认方法
小结:多个接口提供默认方法,则“最具体接口”默认方法胜出,但是不得出现多个“最具体接口”。
情况二:接口IA(或IB1)定义了默认方法m(),类A1相同的方法m(),类C是它们的子类型。
如果类A1提供了实现,按照a simple rule: “the superclass always wins.”),父类的方法 被调用;
如果类A1不提供实现,即A1中m()为抽象方法,仍然按照the superclass always wins.类C需要override m(),给出自己的实现。否则,要么 C声明为抽象类,要么编译错误。
小结:父类有默认方法的等价物,则默认方法如同不存在。
默认方法是对Java语言的有趣补充 – 你可以把他们看做是lambdas表达式和JDK库之间的桥梁。默认表达式的主要目标是使标准JDK接口得以进化,并且当我们最终开始使用Java 8的lambdas表达式时,提供给我们一个平滑的过渡体验。谁知道呢,也许将来我们会在API设计中看到更多的默认方法的应用。
http://www.oschina.net/translate/java-8-explained-default-methods
Java8接口中的默认方法的更多相关文章
- Java8新特性Optional、接口中的默认方法与静态方法
Optional Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念 ...
- 【Java8新特性】接口中的默认方法和静态方法,你都掌握了吗?
写在前面 在Java8之前的版本中,接口中只能声明常量和抽象方法,接口的实现类中必须实现接口中所有的抽象方法.而在Java8中,接口中可以声明默认方法和静态方法,本文,我们就一起探讨下接口中的默认方法 ...
- JDK8.0接口中的默认方法和静态方法
我们在接口中通常定义的方法是抽象方法,即没有方法体,只有返回值类型和方法名:(public abstract) void Method(); 类在实现接口的时候必须重写抽象方法才可以 jdk8中新加的 ...
- Java 8 接口中的默认方法与静态方法
Java 8 接口中的默认方法与静态方法 1. 接口中的默认方法 允许接口中包含具有具体实现的方法,该方法称"默认方法",默认方法使用用 default 关键字修饰. public ...
- Java8新特性_接口中的默认方法
默认方法由来猜想 1. Collection接口.Collections公共类. 同是操作集合,为啥要搞俩?没必要.在接口中搞一些默认实现,一个接口即搞定了. 2. Java8支持Lambda表达式 ...
- JDK8-lambda表达式以及接口可以定义默认方法
一.Lambda表达式 1.Lamdba Lambda 允许把函数作为一个方法的参数,使用Lamdba可以让开发的代码更加简洁,但是易读性差,新人不了解Lamdba表达式或者代码功底有点差,不容易读懂 ...
- Java8中的默认方法
作者:汤圆 个人博客:javalover.cc 前言 大家好啊,我是汤圆,今天给大家带来的是<Java8中的默认方法>,希望对大家有帮助,谢谢 文章纯属原创,个人总结难免有差错,如果有,麻 ...
- Java8:纠结的默认方法
[编程导论(Java)·4.3Java接口] 在[0.3.1 Java简单介绍]中,有这么一段话:"请注意:Java并不是作为教学语言设计的.世界各地的大学在讲授Java的过程中均遇到一些教 ...
- Java8新特性系列-默认方法
Java8 Interface Default and Static Methods 原文连接:Java8新特性系列-默认方法 – 微爱博客 在 Java 8 之前,接口只能有公共抽象方法. 如果不强 ...
随机推荐
- vagrant 相关记录
最近安装vagrant 出错的最大的可能性是BOX 路径不太对, 好像和目录的大小写有关系,请检查 $ vagrant init # 初始化$ vagrant up # 启动虚拟机$ vagrant ...
- 关于_WIN32_WINNT的含义
在使用一些新版本的API,或者控件的新特性(比如新版的ComCtl32.dll)的时候,你可能会得到“error C2065: undeclared identifier.“这个错误.原因是这些功能是 ...
- 【ZZ】MySQL 索引优化全攻略 | 菜鸟教程
MySQL 索引优化全攻略 http://www.runoob.com/w3cnote/mysql-index.html
- Ngnix常用的操作
Nginx的常用参数如下: # /usr/local/nginx/sbin/nginx -h nginx version: nginx/0.7.63 Usage: nginx [-?hvVt] [-s ...
- Educational Codeforces Round 37-F.SUM and REPLACE题解
一.题目 二.题目链接 http://codeforces.com/contest/920/problem/F 三.题意 给定$N$个范围在$[1, 1e6)$的数字和$M$个操作.操作有两种类型: ...
- solr之高级查询--联表 join查询
例如有两个业务表:文章表,评论表 . 场景: 一个文章可以由多个人评论. 创建两个core,一个core叫article,一个叫comment.article实例的schema.xml文件中定义几个简 ...
- 实现PPT在线预览,PPT转图片方案
一.PPT转图片可行方案探索历程 PPT转图片方案 方案具体步骤及分析 已放弃方案 poi(失真度太高):Aspose直接转图片(收费,效果较好,备选):微软Office Online(需要基于Win ...
- 两种定时器 setInterval(一直执行) setTimeout(只执行一次)
//第一种 var obj = setInterval(function () { console.log(123); clearInterval(obj) }, 1000); //第二种 var t ...
- _kbhit() for linux
传送门:http://cboard.cprogramming.com/c-programming/63166-kbhit-linux.html #include <stdio.h> #in ...
- 8 MySQL--单表查询
单表查询: http://www.cnblogs.com/linhaifeng/articles/7267592.html 1.单表查询的语法 2.关键字的执行优先级(重点) 3.简单查询 4.whe ...