接口和内部类为我们提供了一种将接口和实现分离的更加结构化的方法。Java是单继承的,但是通过接口和内部类变让我们基本实现类似多继承的效果。
接口
抽象类与接口
什么是抽象类
抽象类简单的说就是包含抽象方法或类的定义前有abstract关键字的类就是抽象类。那什么是抽象方法呢?抽象方法是只只有申明没有方法体的方法,如下:
1 | abstract void f(); |
其中abstract关键字是必须的。只要是类的定义包含了abstract但是却没有抽象方法,意味着这个类就不想被直接通过构造函数创建实例。如果有抽象方法那么类肯定就是抽象类,也必须包含abstract关键字,例如:
1 | public abstract class User{ |
当然抽象类也能包含非抽象的方法以及字段,也就是说除了包含抽象方法以及不能直接通过构造函数创建实例外与其他类是一样的。
什么是接口
接口的申明其实就是使用interface关键字代替class关键字,而接口的内部只能包含抽象方法以及静态常量(也可以都没有,只是定义)。
1 | public interface ICanFly{ |
比起抽象类来说,接口更加抽象,接口中的字段隐式的确定为public和static以及final的,所以定义是就需要赋值。抽象方法也是隐式申明为public的。
接口与抽象类如何选择
除了申明方式有明显不同外,在设计时我们应该从需求上看,到底需要什么。
- 从抽象内容来看,抽象类抽象的是数据和行为,而接口抽象的只有行为
- 从关系来看,抽象类和子类更多的是”is-a”的关系,接口只是定义具有某个能力并没有特别紧密的从属关系
当然抽象类与接口都各有优点,在使用上优先定义接口,如果有多个接口实现有公用的部分,则使用抽象类,然后集成它。也就是说设计其实也有个重构的过程,并非一开始就带有假设的去抽象,那样的设计只会复杂化,也没有太大的意义。
接口的继承
Java的类是单继承的,而通过接口便可实现类似多继承的效果。如果实现的接口的方法,和继承的类的方法,有相同的,即使子类不实现那个接口的方法,也是可行的,而父类的那个方法就作为实现,例如:
1 | interface CanFight { |
使用接口和类的继承相同的功能在于,可以向上转型,由此只要实现了某个接口,便能传入以该接口为参数的方法中,灵活性大大提高。使用接口的另一个目的也是为了防止直接创建实例。
接口之间还可以通过继承来扩展,例如:
1 | interface CanSwim { |
接口的继承就像是满足多继承一样,使用extends关键字,接口之间用逗号隔开。
如果在接口实现多继承时,不同接口的方法的签名相同但是返回类型不同,那么就会导致出错。例如:
1 | interface CanSwim { |
同时实现这两个出现的错就好比返回值不同的两个fly()方法放在同一个类中一样,会导致编译出错。所以应该避免这种情况。
策略模式
策略模式是指实现某个功能所需的算法有很多种,然后将每个实现方式封装起来,而且这些算法可相互替换,让这些算法提供给使用者自己选择。
根据这种设计模式的含义,很明显这个和向上转型有关联,所以可以使用继承或者实现接口的方式就能很好的设计出这种模式。
适配器模式
适配器模式是指将一个接口转换成客户端希望的另一个接口,使得因为接口不兼容而不能一起工作的类一起使用。
策略模式中发现通过继承和实现接口都是能完成对某些代码的复用,而一旦接口不一致时,通过继承就很难复用,因为Java的单继承,而如果使用接口的话,再多创建一个适配器,变可服用代码。
工厂模式
工厂模式是用于创建对象的一种方式,它会隐藏创建对象的过程,并通过使用共同的接口来指向新对象。
通过这种模式,我们就完全将接口和实现相分离,方便我们更换实现,也不会影响使用,大大提高程序的灵活性。
总结
上文中简单介绍了策略模式,适配器模式以及工厂模式,对于设计模式具体的文章之后会有更为详细的文章来总结。
虽然接口的使用会给程序带来极大的便利,但是我们不能什么都不管直接就使用接口,例如某个接口只是为了以防万一新增内容时方便扩展而设计,目前的需求中不涉及,那么这时候就应该优先考虑直接使用类,等到需要的时候,再重构。但是如果肯定这个以后会有扩展那么也就应该使用接口,而不是等需要时再重构。这就是一个度的把握,而不是滥用。
内部类
内部类就是将一个类的定义放在一个类的定义内部。内部类可以访问外部类的所有字段和方法,这是因为内部类在被创建时就会持有一个外围类的对象引用,所以可以知道,内部类的创建是依赖于外部类的创建的。而有了外部类就可通过.new的方式在外围创建内部类。例如:
1 | public class Chapter10 { |
内部类与向上转型
一旦内部类向上转型为父类或者接口,就能做到类似于多继承的效果,因为内部类是可以访问外部类的所有成员的,而且如果给内部类加上private关键字,就阻止了对这个内部类的扩展,就避免了向下转型的可能。例如:
1 | public class Chapter10 { |
嵌套类
将内部类申明为static就成了嵌套类。嵌套类与内部类的区别:
- 不需要外围类的对象引用
- 不能访问外围类的非静态成员
为什么需要内部类
每个内部类都能独立继承自一个接口的实现,所以无论外围类是否已经继承或实现了某个接口,对于内部类都是没有影响的。
所以通过内部类,就能继承多个非接口的类。也可以在一个类中让多个内部类对一个接口进行不同的实现。
总结
对于内部类和接口都是为了弥补Java不能多继承而存在的特性。读完内部类这一章后感觉对于内部类的理解还有待加强,虽然它有很多优点,但是在Android开发中使用过程中也正是因为持有外部类的对象引用,所以容易导致内存泄漏,这也是需要注意的。