Effective Java---20 接口优于抽象类

前言

Java有两种机制允许多个类型的实现来进行扩展:接口和抽象类。抽象类即用abstract关键字修饰的类,它的特点是可以存在抽象方法,也可以存在普通方法;接口是用interface关键字修饰的,在Java 8之前只能有抽象方法,在Java 8中允许使用default关键字修饰的普通方法存在,在结构上和抽象类类似,但是抽象类有构造方法的,虽然不能直接实例化,接口就没有。

Java的单继承多实现

接口优于抽象类的最主要原因就是Java是单继承的,在一定程度上限制了扩展性,限制了它们作为类型定义的使用,我们不能保证在编程后续过程中是否需要进行扩展,所以不会轻易使用抽象类来进行扩展。

接口的存在就很好的解决了上面的问题,Java可以通过接口多实现。现有的类可以很容易地实现一个新的接口进行扩展。 你只需添加所需的方法(如果尚不存在的话),并向类声明中添加一个 implements 子句。 例如,当 Comparable, Iterable, 和 Autocloseable 接口添加到 Java 平台时,很多现有类需要实现它们来加以改进。 一般来说,现有的类不能改进以继承一个新的抽象类。 如果你想让两个类继承相同的抽象类,你必须把它放在类型层级结构中的上面位置,它是两个类的祖先。 不幸的是,这会对类型层级结构造成很大的附带损害,迫使新的抽象类的所有后代对它进行子类化,无论这些后代类是否合适。

接口是定义混合类型(mixin–mix in混入)的理想选择。 一般来说,mixin 是一个类,除了它本身的“主类型”之外,还可以声明它提供了一些可选的行为。 例如,Comparable 是一个类型接口,它允许一个类声明它的实例相对于其他可相互比较的对象是有序的。 这样的接口被称为类型,因为它允许可选功能被“混合”到类型的主要功能。简单点就是接口A能根据需要同时继承接口B和接口C成为混合类型; 抽象类不能用于定义混合类,这是因为它们不能被加载到现有的类中:一个类不能有多个父类,并且在类层次结构中没有合理的位置来插入一个类型。

接口允许构建非层级类型的框架。 类型层级对于组织某些事物来说是很好的,但是其他的事物并不是整齐地落入严格的层级结构中。 例如,假设我们有一个代表歌手的接口,和另一个代表作曲家的接口:

public interface Singer {
    AudioClip sing(Song s);
}

public interface Songwriter {
    Song compose(int chartPosition);
}

在现实生活中,一些歌手也是作曲家。 因为我们使用接口而不是抽象类来定义这些类型,所以单个类实现歌手和作曲家两个接口是完全允许的。 事实上,我们可以定义一个继承歌手和作曲家的第三个接口,并添加适合于这个组合的新方法:

public interface SingerSongwriter extends Singer, Songwriter {
    AudioClip strum();
    void actSensitive();
}

使用默认方法可以提供实现帮助多多少少是有些限制的。 尽管许多接口指定了 Object 类中方法(如 equalshashCode)的行为,但不允许为它们提供默认方法。 此外,接口不允许包含实例属性或非公共静态成员(私有静态方法除外)。 最后,不能将默认方法添加到不受控制的接口中。

骨架实现类

在Effective Java中提到了一个所谓的骨架实现类,理解起来总是怪怪的:使得具体子类只专注于自身的特定方法实现,忽略自身不会提供的方法(对于自身的冗余方法).简单说就是没必要一个个实现接口的所有方法,要调用就调用骨架类的。Java源码中AbstractCollection实现了Collection接口,List接口继承自Collection接口,AbstractList又继承了AbstractCollection类,并且实现了List接口。而这里ArrayList不允许忽略任何list接口的方法,必须自己去小心实现每个方法.当有某类只继承AbstractList,那就方便了,只要改写需要改写的部分,不需要使用或者修改的地方可以直接复用骨架实现类的实现。

还有一个用内部类模拟Java多继承:
儿子是如何利用多重继承来继承父亲和母亲的优良基因。

首先是父亲Father和母亲Mother:

public class Father {
    public int strong(){
        return 9;
    }
}
public class Mother {
    public int kind(){
        return 8;
    }
}

重头戏在这里,儿子类Son:

public class Son {
    
    /**
     * 内部类继承Father类
     */
    class Father_1 extends Father{
        public int strong(){
            return super.strong() + 1;
        }
    }
    
    class Mother_1 extends  Mother{
        public int kind(){
            return super.kind() - 2;
        }
    }
    
    public int getStrong(){
        return new Father_1().strong();
    }
    
    public int getKind(){
        return new Mother_1().kind();
    }
}

儿子继承了父亲,变得比父亲更加强壮,同时也继承了母亲,只不过温柔指数下降了。这里定义了两个内部类,他们分别继承父亲Father类、母亲类Mother类,且都可以非常自然地获取各自父类的行为,这是内部类一个重要的特性:内部类可以继承一个与外部类无关的类,保证了内部类的独立性,正是基于这一点,多重继承才会成为可能。


参考文章:
https://blog.csdn.net/jiafu1115/article/details/6736063
https://www.cnblogs.com/qlky/p/7350740.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抽象类和接口的区别 【概述】 一个软件设计的好坏,我想很大程度上取决于它的整体架构, 而这个整体架构其实就是你对整个宏观商业业务的抽象框架,当代表业务逻辑的高层抽象层结构 合理时, 你底层的具体实现需要考虑的就仅仅是一些算法和一些具体的业务实现了。 当你需要再开发另一个相近的项目时,你以前的抽象层说不定还可以再次利用 。 面向对象的设计,复用的重点其实应该是抽象层的复用,而不是具体某一个代码块的复用。 在 面向对象的概念中,我们知道所有的对象都是通过类来描绘的,但是反过来却不是这样。 并不是 所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象, 这样的类就是抽象类。抽象类往往用来表征我们在对问题领域进行分析、 设计中得出的抽象概念, 是对一系列看上去不同,但是本质上相同的具体概念的抽象。 比如:如果我们进行一个图形编辑软件的开发,就会发现问题领域存在着圆、 三角形这样一些具体概念, 它们是不同的,但是它们又都属于形状这样一个概念,形状这个概念在问题领域是不存在的, 它就是一个抽象概念。正是因为抽象的概念 在问题领域没有对应的具体概念, 所以用以表征抽象概念的抽象类是不能够实例化的。 在面向对象领域,抽象类主要用来进行类型隐藏。 我们可以构造出一个固定的一组行为的抽象描 述, 但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类, 对于面向对象编程来说,抽象是它的一大特征之一。在Java中,可以通过两种形式来体现OOP(Object Oriented Programming,OOP,面向对象程序设计)的抽象接口抽象类。 这两者有太多相似的地方,又有太多不同的地方。很多人在初学的时候会以为它们可以随意互换使用,但是实际则不然。 今天我们就一起来学习一下Java中的接口抽象类。 【抽象类】 在了解抽象类之前,先来了解一下抽象方法。 抽象方法是一种特殊的方法:它只有声明,而没有具体的实现。抽象方法的声明格式为: abstract void fun(); 抽象方法必须用abstract关键字进行修饰。如果一个类含有抽象方法,则称这个类为抽象类, 抽象类必须在类前用abstract关键字修饰。因为抽象类中含有无具体实现的方法,所以不能用抽象类创建对象。 抽象类不一定必须含有抽象方法。比如: 复制代码 public abstract class Dog { //普通方法 void bark() { } //抽象方法 abstract void Play(); } 复制代码 [public] abstract class ClassName { abstract void fun(); } 从这里可以看出,抽象类就是为了继承而存在的,如果你定义了一个抽象类,却不去继承它, 那么等于白白创建了这个抽象类,因为你不能用它来做任何事情。对于一个父类, 如果它的某个方法在父类中实现出来没有任何意义,必须根据子类的实际需求来进行不同的实现, 那么就可以将这个方法声明为abstract方法,此时这个类也就成为abstract类了。   包含抽象方法的类称为抽象类,但并不意味着抽象类中只能有抽象方法,它和普通类一样, 同样可以拥有成员变量和普通的成员方法。注意,抽象类和普通类的主要有三点区别:   1)抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法), 缺省情况下默认为public。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值