【编程导论(Java)·4.3Java接口】

在【0.3.1 Java简单介绍】中,有这么一段话:“请注意:Java并不是作为教学语言设计的。世界各地的大学在讲授Java的过程中均遇到一些教学上的困难(如Java语言和API的高速升级),这些困难是计算机科学教育中一般性的挑战。”

Java8中引入的默认方法,充分展示了Java平台中概念的一致性JDK向前兼容之间的矛盾,并且悲哀地。以牺牲概念的一致性而满足JDK向前兼容。

1.理想与现实

【以前】Java接口纯粹是契约的集合,是一种程序设计的表达方式。从数据抽象的角度看,可以在不定义class的同一时候定义type。将是程序设计中强大而实用的机制。Java接口就是这些纯粹的接口组成的数据抽象。

Java接口仅仅可以拥有抽象方法,它不涉及不论什么实现,也不能创建其对象(这一点和抽象类一致)。

多重继承模型导致额外的复杂性,当中最著名的是钻石问题或者叫“讨嫌的菱形派生”(Dreadful Diamond onDerivation、DDD)。为什么Java接口可以避免多继承的复杂性,关键在于它只包括abstract方法。

然而从设计的角度看,Java接口放弃了多继承的内在/固有目标。而显得是一个权宜之计。

【如今】Java8之前,接口不能升级。由于在接口中加入一个方法,会导致老版本号接口的全部实现类的中断。

λ表达式作为核心出现。为了配合λ表达式,JDK中Collection库须要加入新的方法。如forEach(),stream()等。于是引入了默认方法(defender methods,Virtual extension methods)。它是库/框架设计的程序猿的懊悔药。对于曾经的遗留代码。大家都不知道有这个新方法,既不会调用,也不会去实现。如同不存在。编写新代码的程序猿能够将它视为保底的方法体。类型层次中不论什么符合override规则的方法,优先于默认方法,由于遗留代码可能正好有相同的方法存在。

默认方法。理论上抹杀了Java接口与抽象类的本质差别——前者是契约的集合,后者是接口与实现的结合体。当然。语法上两者的差别和曾经一样。这就须要程序猿来自觉维护两者的本质差别。把默认方法作为库、框架向前兼容的手段

2.抽象类 Vs 函数接口

在默认方法的使用上,还存在一种刻意的抹杀。

原本应该设计为抽象类,为了方便使用lambda,而设计成函数接口。

public abstract class AbstractClass {
public abstract void foo();
public final void m1() {
}
}

在AbstractClass类中,m()方法能够设计为final,而为了使用lambda,该方法变成了default方法——而在不论什么情况下,我们都不会override该default方法。

public interface NewInterface {
public abstract void foo();
public default void m1(){ }
}

JDK中非常多default方法,事实上就应该是某个抽象类的final方法;Java 8不同意我们在default方法前加上final。

为了使用lambda。这样真的好吗?我不知道怎样评价。

  • final体现了我们的设计意图。default方法说明lambda表达式的目标类型须要这些方法(即使子类型不须要override)
  • 抽象类包括接口和实现; 函数接口,为lambda作准备。
package defaultMethodDemo;
import static util.Print.pln;
public class Test {
public static void test(AbstractClass a){
a.foo();
}
public static void test(NewInterface f){
f.foo();
} public static void main(String[] args){
AbstractClass a = new AbstractClass(){
@Override public void foo(){pln("AbstractClass");}
};
test(a);
test(()->pln("NewInterface"));
}
}

还有一方面,假设一个抽象类由一个抽象方法加其它静态方法构成。则将它设计为函数接口,比較合理。比如例程11-1的IntSort

2.钻石问题

【默认方法的一个优点:多继承的著名的是钻石问题(The Diamond Problem )再次须要关注。因而使曾经某些人觉得的“为了解决多继承问题而引入接口机制”的说法变成明显的错误——曾经也是错误的认识。

情况一:接口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(),调用“最详细接口”默认方法。

package java8;
public interface IB2 extends IA{
@Override default void m(){
System.out.println("IB2");
}
}

3)假设IB1、IB2都override IA定义的m(),而C不提供自己的方法体,则编译错误。由于 “最详细接口”默认方法有两个。

此时,C若Override m()。能够消去编译错误

4)类C提供自己的方法体时,能够提供自己的代码,也能够指定调用C implements 的直接父接口的默认方法,代码例如以下:

package java8;
public class C implements IB1, IB2{
@Override public void m(){
IB2.super.m();
// IA.super.m();
};
public static void main(String[] a) {
new C().m();
}
}

小结:多个接口提供默认方法。则“最详细接口”默认方法胜出,可是不得出现多个“最详细接口”。

情况二:接口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声明为抽象类,要么编译错误。

小结:父类有默认方法的等价物,则默认方法如同不存在。

Java8:纠结的默认方法的更多相关文章

  1. Java8中的默认方法

    作者:汤圆 个人博客:javalover.cc 前言 大家好啊,我是汤圆,今天给大家带来的是<Java8中的默认方法>,希望对大家有帮助,谢谢 文章纯属原创,个人总结难免有差错,如果有,麻 ...

  2. java8新特性-默认方法

    作为一个java程序猿,经常会被问基础怎么样,对于这个问题,我理解的有两方面:一是对于java基础的理解和掌握,比如JDK的相关特性:二是工作的经历,毕竟,语言编程是一门实战性质的艺术,就算掌握了千万 ...

  3. java8新特性- 默认方法 在接口中有具体的实现

    案例分析 在java8中在对list循环的时候,我们可以使用forEach这个方法对list进行遍历,具体代码如下demo所示 public static void main(String[] arg ...

  4. Java8 新特性 默认方法

    默认方法为什么出现 默认方法的出现是因为在java8设计的过程中,因为加入了Lamdba表达式,和函数式接口,所以在非常多的接口里面要加入新的方法,但是如果在接口里面直接加入新的方法,那么以前写的所有 ...

  5. Java8接口的默认方法

    项目实战 实现上图接口的实现类有很多,其中有些实现类已经在生成环境了,现在需要新增几个实现类,都需要有回调方法,所以在接口中添加了一个回调的默认方法,如果使用接口的普通方法就得改所有实现了接口的实现类 ...

  6. 【Java8新特性】接口中的默认方法和静态方法,你都掌握了吗?

    写在前面 在Java8之前的版本中,接口中只能声明常量和抽象方法,接口的实现类中必须实现接口中所有的抽象方法.而在Java8中,接口中可以声明默认方法和静态方法,本文,我们就一起探讨下接口中的默认方法 ...

  7. java 8 接口默认方法

    解决问题:在java8 之前的版本,在修改已有的接口的时候,需要修改实现该接口的实现类. 作用:解决接口的修改与现有的实现不兼容的问题.在不影响原有实现类的结构下修改新的功能方法 案例: 首先定义一个 ...

  8. Java8新特性——接口的默认方法和类方法

    Java8新增了接口的默认方法和类方法: 以前,接口里的方法要求全部是抽象方法,java8以后允许在接口里定义默认方法和类方法: 不同的是: 默认方法可以通过实现接口的类实例化的对象来调用,而类方法只 ...

  9. 30分钟入门Java8之默认方法和静态接口方法

    30分钟入门Java8之默认方法和静态接口方法 前言 上一篇文章30分钟入门Java8之lambda表达式,我们学习了lambda表达式.现在继续Java8新语言特性的学习,今天,我们要学习的是默认方 ...

随机推荐

  1. 使用AutoMapper 处理DTO数据对象的转换

    using AutoMapper;using System; namespace DTOtEST{ class Program { static void Main(string[] args) { ...

  2. k8s的Rolling Update(滚动更新应用)

    滚动更新是一次只更新一小部分副本,成功后,再更新更多的副本,最终完成所有副本的更新.滚动更新的最大的好处是零停机,整个更新过程始终有副本在运行,从而保证了业务的连续性. 下面我们部署三副本应用: 初始 ...

  3. python类的__new__和__init__

    python的类,和其他语言有一点不太一样,就是,他把新建一个类和初始化一个类,分成了两个方法: __new__ __init__ 当然,想想就知道,肯定是__new__先发生,然后才是__init_ ...

  4. laravel将数据库对象转为数组的方法

    亲测有效 array_map('get_object_vars', $data);

  5. AC日记——[ZJOI2007]报表统计 bzoj 1058

    1058 思路: 平衡树的题: 然而我的平衡树写一次炸一次QwQ: 而且各种tle: 所以stl水过: 代码: #include <set> #include <cstdio> ...

  6. linux+win7双系统重装win7修复grub的办法

    本人是debian+win7的双系统, 下面介绍下重装win7的整个过程以及遇到的一些小问题,在查阅相关博客和朋友的帮助下成功修复, 记录下以便以后有不时之需, 也希望能帮助到遇到同样问题的朋友! 首 ...

  7. Servlet 调用过程

    上图的大概意思: 前台输入访问路径后,浏览器会去访问本地的host文件查询有木有与之匹配域名的IP地址,若无则在访问DNS服务器查询与之匹配的IP地址.解析IP后则开始发起HTTP请求,根据请求中的基 ...

  8. HDU 2256 Problem of Precision (矩阵快速幂)(推算)

    Problem of Precision Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Oth ...

  9. [BZOJ 1799] self 同类分布

    Link: BZOJ 1799 传送门 Solution: 一句话的题目,看得爽,做得烦 一般这类和数位相关的都是数位$dp$吧 不过一开始还是感觉不太可做,毕竟每个数模数不同 但要发现,模数最高也只 ...

  10. bzoj 5347: 冒泡排序

    考虑到最后a[i]都要等于i,并且每个 a[i] < i 的a[i] 一轮最多向前走一次,所以局数至少是 max{ i - a[i] }. 又因为对于a[i] < i来说,一轮不动意味着 ...