关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » JAVA »Java 8 默认方法和多继承深入解析

    Java 8 默认方法和多继承深入解析

    2015-05-22 00:00:00 出处:ImportNew
    分享

    以前经常谈论的Java对比c++的一个优势是Java中没有多继承的问题。 因为Java中子类只能继承(extends)单个父类, 尽管可以实现(implements)多个接口,但是接口中只有抽象方法,方法体是空的,没有具体的方法实现,不会有方法冲突的问题。

    这些都是久远的说法了,自从今年Java 8发布后, 接口中也可以定义方法了(default method)。 之所以打破以前的设计在接口中
    增加具体的方法, 是为了既有的成千上万的Java类库的类增加新的功能, 且不必对这些类重新进行设计。 比如, 只需在Collection接口中
    增加default Stream<E> stream(), 相应的Set和List接口以及它们的子类都包含此的方法, 不必为每个子类都重新copy这个方法。

    这是一个折衷的设计,带来的问题就是为Java引入了多继承的问题。 我们知道, 接口可以继承接口, 类可以继承类和实现接口。 一旦继承的类和实现的接口中有相同签名的方法, 会出现什么样的状况呢? ITJS的这篇文章将探讨各种情况的多继承, 以便能清楚的理解Java多继承的规则。

    接口继承多个父接口

    假定有三个接口Interface A, Interface B, Interface C, 继承关系如下:

    +---------------+         +------------+
    |  Interface A  |         |Interface B |
    +-----------^---+         +---^--------+
                |                 |         
                |                 |         
                |                 |         
                +-+------------+--+         
                  | Interface C|            
                  +------------+

    A,B拥有相同签名的默认方法default String say(String name), 如果接口C没有override这个方法, 则编译出错。

     interface A {
    	default String say(String name) {
    		return "hello " + name;
    	}
    }
    interface B {
    	default String say(String name) {
    		return "hi " + name;
    	}
    }
    interface C extends A,B{
    
    }

    错误信息:

    C:/Lambda/src>javac -J-Duser.country=US com/colobu/lambda/chap
    ter3/MultipleInheritance1.java
    com/colobu/lambda/chapter3/MultipleInheritance1.java:17: error: interface C inherits unrelated defaults for say(String) from types A and B
            static interface C extends A,B{
                   ^
    1 error

    我们可以在子接口C中覆盖override这个方法, 这样编译就不会出错了:

    interface C extends A,B{
    	default String say(String name) {
    		return "greet " + name;
    	}
    }

    注意方法签名不包括方法的返回值, 也就是仅仅返回值不同的两个方法的签名也是相同的。下面的代码编译不会出错,因为A和B的默认方法不同, C隐式继承了两个默认方法。

    interface A {
    	default void say(int name) {
    
    	}
    }
    interface B {
    	default void say(String name) {
    
    	}
    }
    interface C extends A,B{
    
    }

    但是有的情况下即使是不同签名的方法也是很难分辨的:

    interface A {
    	default void say(int a) {
    		System.out.println("A");
    	}
    }
    interface B {
    	default void say(short a) {
    		System.out.println("B");
    	}
    }
    interface C extends A,B{
    
    }
    static class D implements C {
    
    }
    public static void main(String[] args) {
    	D d = new D();
    	byte a = 1;
    	d.say(a); //B
    }

    Java会选择最适合的方法, 请参看Java规范 15.12.2.5

    接口多层继承

    下面看一下多层继承的问题。 继承关系如下图, A2继承A1, C继承A2。

    +---------------+ 
    |  Interface A1 | 
    +--------+------+ 
             |        
             |        
             |        
    +--------+------+ 
    |  Interface A2 | 
    +-------+-------+ 
            |         
            |         
            |         
    +-------+--------+
    |   Interface C  |
    +----------------+

    基于我们以前对类继承的认识, 很容易知道C会继承A2的默认方法,包括直接定义的默认方法, 覆盖的默认方法,以及隐式继承于A1接口的默认方法。

    interface A {
    	default void say(int a) {
    		System.out.println("A");
    	}
    
    	default void run() {
    		System.out.println("A.run");
    	}
    }
    interface B extends A{
    	default void say(int a) {
    		System.out.println("B");
    	}
    
    	default void play() {
    		System.out.println("B.play");
    	}
    }
    interface C extends A,B{
    
    }

    多层多继承

    上面一个例子还是单继承的例子, 如果如下图的多继承呢?

    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    +---------------+                          
    |  Interface A1 |                          
    +--------+------+                          
             |                                 
             |                                 
             |                                 
    +--------+------+         +---------------+
    |  Interface A2 |         |  Interface B  |
    +-------+-------+         +---------+-----+
            |       +---------+---------^      
            |       |                          
            |       |                          
    +-------+-------++                         
    |   Interface C  |                         
    +----------------+

    如果A2和B拥有相同签名的方法,这和第一个例子一样。 如果不想编译出错,可以覆盖父接口的默认方法,还可以调用指定父接口的默认方法:

    interface A1 {
    		default void say(int a) {
    			System.out.println("A1");
    		}
    	}
    
    	interface A2 extends A1 {
    
    	}
    
    	interface B {
    		default void say(int a) {
    			System.out.println("B");
    		}
    	}
    	interface C extends A2,B{
    		default void say(int a) {
    			B.super.say(a);
    		}
    	}

    更复杂的多层多继承

    +--------------+              
     | Interface A1 |              
     +------+------++              
            |      ^+-------+      
            |               |      
    +-------+-------+       |      
    |  Interface A2 |       |      
    +------------+--+       |      
                 ^--++      |      
                     |      |      
                  +--+------+-----+
                  |  Interface C  |
                  +---------------+

    接口A2继承A1, 接口C继承A2和A1。 代码如下,

    interface A1 {
    	default void say() {
    		System.out.println("A1");
    	}
    }
    interface A2 extends A1 {
    	default void say() {
    		System.out.println("A2");
    	}
    }
    interface C extends A2,A1{
    
    }
    static class D implements C {
    
    }
    public static void main(String[] args) {
    	D d = new D();
    	d.say();
    }

    以上代码不会编译出错,运行输出A2。

    可以看到接口C会隐式继承子接口的方法, 也就是子接口A2的默认方法。

    类继承

    如果继承关系类型全部是类, 那么由于类依然是单继承的, 不会有多继承的问题。

    类和接口混杂

    我们把第一个例子中的其中一个接口换成类,会出现什么现象呢。

    +-------------+       +-----------+
    | Interface A |       |  Class B  |
    +-----------+-+       +-----+-----+
                ^-+    +--+-----^      
                  |    |               
              +---+----+-+             
              |  Class C |             
              +----------+

    以下代码不会编译出错:

    interface A {
    	default void say() {
    		System.out.println("A");
    	}
    }
    static class B {
    	public void say() {
    		System.out.println("B");
    	}
    }
    static class C extends B implements A{
    
    }
    public static void main(String[] args) {
    	C c = new C();
    	c.say(); //B
    }

    结果输出B。

    可以看出, 子类优先继承父类的方法, 如果父类没有相同签名的方法,才继承接口的默认方法。

    结论

    更复杂的继承关系可以简化成以上的继承关系。
    根据以上的例子, 可以得出以下的结论:

    类优先于接口。 如果一个子类继承的父类和接口有相同的方法实现。 那么子类继承父类的方法 子类型中的方法优先于父类型中的方法。 如果以上条件都不满足, 则必须显示覆盖/实现其方法,或者声明成abstract。
    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    DESTOON标签(tag)调用手册说明(最新版)

    Destoon 9.0全站伪静态规则设置清单(Apache版)

    Destoon 9.0全站伪静态规则设置清单(Nginx版)

    Destoon 8.0全站伪静态规则设置清单(Apache版)

    Destoon 8.0全站伪静态规则设置清单(Nginx版)

    Destoon会员公司地址伪静态com/目录如何修改?两步轻松搞定,适合Nginx和Apache

    Python 并行处理列表的常见方法及其优缺点分析

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    IT头条

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    技术热点

    最常用的 Eclipse 快捷键整理

    多表多查询条件对SQL Server查询性能的优化

    浅谈如何优化SQL Server服务器

    HTTP 协议中使用 Referer Meta 标签控制 referer

    好用的mysql备份工具

    Android开发中的MVP架构详解

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。