【编程导论(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. 【C++】各种成员变量

    来自:黄邦勇帅 const 常量对象: 即把对象声明为常量,即 const hyong m,常量对象不能调用可能改变对象的值的函数,因此常量对象只能调用类中的 const 常量函数,因为不是 cons ...

  2. docker从零开始 存储(四)tmpfs挂载

    使用tmpfs挂载 volume和bind mounts允许您在主机和容器之间共享文件,以便即使在容器停止后也可以保留数据. 如果你在Linux上运行Docker,你有第三个选择:tmpfs moun ...

  3. 作业执行器Job Executor

    Job Executor 激活作业执行器 AsyncExecutor是一个组件,它管理线程池,来触发计时器和其他异步任务.其他实现也是可能的(例如使用消息队列,请参阅用户指南的高级部分). 默认情况下 ...

  4. SVN的配置和使用

    1.安装前必备 获取 Subversion 服务器程序 到官方网站 http://subversion.tigris.org/    我下的是CollabNetSubversion-server-1. ...

  5. AC日记——Rmq Problem bzoj 3339

    3339 思路: 恶心: 代码: #include <cstdio> #include <cstring> #include <iostream> #include ...

  6. Linux 下 UltraEdit 版本 破解 30 天试用限制

    原创 http://yhz61010.iteye.com/blog/2319599 rm -rfd ~/.idm/uex rm -rf ~/.idm/*.spl rm -rf /tmp/*.spl 一 ...

  7. NetTcpBinding 类nettcpbinding的属性和方法

      一种适合于跨计算机通信的安全可靠的绑定. 继承层次结构   System.Object   System.ServiceModel.Channels.Binding    System.Servi ...

  8. LCA+差分【CF191C】Fools and Roads

    Description 有一颗 \(n\) 个节点的树,\(k\) 次旅行,问每一条边被走过的次数. Input 第一行一个整数 \(n\) (\(2\leq n\leq 10^5\)). 接下来 \ ...

  9. Fruit Feast(暴力)(动态规划)

    Fruit Feast 时间限制: 1 Sec  内存限制: 64 MB提交: 64  解决: 18[提交][状态][讨论版] 题目描述 Bessie has broken into Farmer J ...

  10. Counting Haybales (线段树)

    Counting Haybales 时间限制: 50 Sec  内存限制: 256 MB提交: 52  解决: 18[提交][状态][讨论版] 题目描述 Farmer John is trying t ...