在《java编程思想》中有这样一句话,“抽象类是很有用的重构工具,因为它们使得我们可以很容易的将公共方法沿着继承层次结构向上移动。“
(以下文字来自于以往的学习笔记,借鉴了很多网络上优秀的分析和自己的代码经验,如有不足,希望指正)
优秀文章借鉴:http://blog.csdn.net/lyflower/article/details/4204449
http://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html
最近在写一个新闻客户端的例子,在使用fragment的时候,用到了很多fragment,每个fragment我都会去实现,onCreate(),onCreateView(),onActivityCreated()三个方法。所有就干脆把它抽象成了一个基类。
public abstract class BaseFragment extends Fragment { public View view; public Context ct; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); initData(savedInstanceState); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ct = getActivity(); } /** * setContentView; */ @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = initView(inflater); return view; } /** * 初始化view */ public abstract View initView(LayoutInflater inflater); /** * 初始化数据 */ public abstract void initData(Bundle savedInstanceState); }
这里讲一下为什么在我的BaseFragment中弄出了两个抽象方法。首先是
publicabstractView initView(LayoutInflater inflater);
因为在onCreateView方法中我们必须返回一个view对象,所以这个方法的返回值是View类型。在子类中会用到inflater进行构建布局,所以必须把inflater传进来。
public abstractvoidinitData(Bundle savedInstanceState);
初始化数据,不需要返回任何类型的数据。在后续写的类中会用到Bundle的对象,所以也需要传进来。
所以在BaseFragment的子类中我们就可以很方便的只用去复写这两个方法就是,真正的实现了代码的重构和简化。
public class MenuFragment2 extends BaseFragment { @Override public View initView(LayoutInflater inflater) { view = inflater.inflate(R.layout.frag_home2, null); return view; } @Override public void initData(Bundle savedInstanceState) { } }
2 注意
1)某个接口被类实现时,在类中一定要实现接口中所有的抽象方法,
所以才会有在eclipse中我们每次实现实现一个方法的时候它会弹出下面的提示,也就是说我们要嘛选择实现里面的所有未被实现的方法,要嘛将我们现在这个类写作抽象类,由当前类的子类再去实现。
2)抽象方法被继承想调用那个方法就调用那个方法。所以我们经常用到的activity当中,我们可以选择性的实现(下图中的黄色菱形)那些方法。
3)你可以有多个干爹(接口),但只能有一个亲爹( 继承)。
4)设计模式中有提到:
依赖倒转原则: 依赖抽象(例如接口),不要依赖于实现
合成/聚合复用原则(CARP): 尽量使用合成/聚合,而不是继承关系达到复用的目的
5)接口传达的意思是:like-a拥有某种功能,能干嘛,比如:Serializable代表可序列化的
继承传达的是意思是:is-a,比如:猫是一个 动物,猫就是动物的子类
6)在实际开发中,这两个都会大量的用到,接口主要是实现一种松耦合,便于以后的维护、升级,继承主要是提高代码的可重用性,很多东西都可以在父类中做好。
7)抽象类可以包含具体的方法法,变量,常量等...,可以部分包含抽象方法。而接口则是定义全局常量,和抽象方法。
8)抽象类不能被实例化:
定义抽象类水果(Fruit)
public abstract class Fruit {
……
}
如果我们试图用以下语句来获得一个实例,将无法编译成功。
Fruit fruit = new Fruit();
9)如果是抽象类实现一个接口,那么抽象类中可以不具体实现接口的方法(保持其抽象性),而由其子类去实现。
比如:抽象类ClassB实现接口InterfaceA,但是没有具体实现方法methodA(),
public interface InterfaceA { void methodA(); } public abstract class ClassB implements InterfaceA{ } //子类ClassB实现接口InterfaceA,但是没有具体实现方法methodA(), 由它的子类 public class ClassC extends InterfaceA{ public void methodA() { System.out.println( "methodAof ClassC the subclass of ClassB" ); } }
3 举例
1) 你知道开会,一定是先致幕词,然后讨论,然后结束总结。每个会议都有这三个步骤,但是你不知道每一个步骤里都具体要实现什么。此时我们要针对这个会议开发一个适应性很强的程序。最简单就是面向接口编程。我们在主程序中,用接口类型的引用来指向传入的会议对象。任何实现了会议接口3个方法的对象,都可以被你调用。你不需要它具体怎么实现的,或者它的对象具体是哪个地址,只需要对象必须实现了会议接口这三个方法,而你的程序只是顺序调用接口引用指针下的三个方法,就完成了程序。至于传入什么样的会议对象,就放到了程序的外围,符合标准的。传入就可以处理。而继承是为了解决另外的问题。比如已经有一个会议程序了,我们要兼容它,然后还增加我们的自己的接口实现或者工呢。这时我们就使用继承,将已有程序的类继承,然后该覆盖的方法覆盖,并且实现我们需要的接口。然后将子类传入都新写的主类中,依旧遵从面向接口的编程去开发。
2) 如果狗的主人只是希望狗能爬比较低的树,但是不希望它尾巴可以倒挂在树上,像猴子那样可以飞檐走壁,以免主人管不住它。那么狗的主人肯定不会要一只猴子继承的狗。设计模式更多的强调面向接口。猴子有两个接口,一个是爬树,一个是尾巴倒挂。我现在只需要我的狗爬树,但是不要它尾巴倒挂,那么我只要我的狗实现爬树的接口就行了。同时不会带来像继承猴子来带来的尾巴倒挂的副作用。这就是接口的好处。