【编程导论(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. Django组件之contenttype的应用

    contenttypes 是Django内置的一个应用,可以追踪项目中所有app和model的对应关系,并记录在ContentType表中. 每当我们创建了新的model并执行数据库迁移后,Conte ...

  2. Linux下如何批量转码iconv

    来源:http://hi.baidu.com/curioz/blog/item/2555863514f9491d90ef390d.html 下载了不少文本txt,如verycd上的致纯书苑,解压看看是 ...

  3. PHP常用函数及其注释

    <?php //===============================时间日期=============================== //y返回年最后两位,Y年四位数,m月份数字 ...

  4. [BZOJ4199][Noi2015]品酒大会 树形DP+后缀自动机

    由于要找后缀的前缀,所以先用反串建立SAM. link边组成了后缀树. 两个子串的最长公共前缀是LCA的step 树形dp即可. #include<iostream> #include&l ...

  5. AC日记——T-Shirt Hunt codeforces 807b

    T-Shirt Hunt 思路: 水题: 代码: #include <cstdio> #include <cstring> #include <iostream> ...

  6. c# 使用ssh连接远程主机(ssh.net演示)

    本教程使用的是ssh.net这个库.项目地址:https://github.com/sshnet/SSH.NET 使用ssh客户端连接远程主机执行命令,并拿到输出结果: using (var sshC ...

  7. mimikatz-域密码获取神器

    mimikatz是一个法国人写的轻量级调试器.出众之处在于其可以直接从 lsass.exe 里猎取windows处于active状态账号明文密码,非常强大. 在网上找了一些相关的文章自己的一点总结吧 ...

  8. 线段树维护矩阵【CF718C】 Sasha and Array

    Description 有一个长为\(n\)的数列\(a_{1},a_{2}...a_{n}\),你需要对这个数列维护如下两种操作: \(1\space l \space r\space x\) 表示 ...

  9. 洛谷——P2758 编辑距离

    P2758 编辑距离 题目描述 设A和B是两个字符串.我们要用最少的字符操作次数,将字符串A转换为字符串B.这里所说的字符操作共有三种: 1.删除一个字符: 2.插入一个字符: 3.将一个字符改为另一 ...

  10. 23、Flask实战第23天:Flask-Restful

    Restful API规范 restful api是用于前端和后台进行通信的一套规范.使用这个规范可以让前后端开发变得更加轻松. 协议 采用http或者https 数据传输格式 数据之间传输的格式应该 ...