zoukankan      html  css  js  c++  java
  • Java8:纠结的默认方法

    【编程导论(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声明为抽象类,要么编译错误。

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




  • 相关阅读:
    webpack.DefinePlugin
    webpack-dev-server配置指南(使用webpack3.0)
    Eclipse配色方案插件
    解决Sublime Text 3中文显示乱码问题(转)
    Java连接SqlServer2008数据库
    [转]java中判断字符串是否为数字的三种方法
    VS2008 SP1 安装卡在 VS90sp1-KB945140-X86-CHS的解决方法
    Python获取桌面路径
    关于fdisk命令
    socket 错误之:OSError: [WinError 10057] 由于套接字没有连接并且(当使用一个 sendto 调用发送数据报套接字时)没有提供地址,发送或接收数据的请求没有被接受。
  • 原文地址:https://www.cnblogs.com/cxchanpin/p/6796158.html
Copyright © 2011-2022 走看看