Java编程思想(六) —— 接口

来源:互联网 发布:sqlserver 增加字段 编辑:程序博客网 时间:2024/06/05 16:28

接口一章讲到抽象类和接口,单纯看书上的抽象类感觉很抽象。而且接口这一章的知识点,并没有像之前的讲的详细,而且碎且难,有几个问题是几个人在研讨会提出的,之前多态的有一个域的问题也是研讨会有人提出,那个相对容易理解一些。


1)抽象类

class TV{      public void show(){          System.out.println("TV");      }  }    class LeTV extends TV{      public void show(){          System.out.println("LeTV");      }  }  class MiTV extends TV{      public void show(){          System.out.println("MiTV");      }  }  class SanTV extends TV{      public void show(){          System.out.println("SanTV");      }  }  public class EveryTV {      public static void tvshow(LeTV tv){          tv.show();      }      public static void tvshow(MiTV tv){          tv.show();      }      public static void tvshow(SanTV tv){          tv.show();      }      public static void main(String[] args) {          tvshow(new LeTV());          tvshow(new MiTV());          tvshow(new SanTV());      }  }

其实会发现,从TV一开始,并没有创建TV对象,因为TV对象没什么意义。

java提供了一种抽象方法的机制,C++中叫纯虚函数。

包含抽象方法的类为抽象类,如果一个类有一个或多个的抽象方法,那么类一定要定义为抽象类。

抽象的方法声明可以没有方法体。如下:

abstract show();

抽象类不可new,确保抽象类的纯粹性,这就是抽象类,其实这也是抽象类存在的一个原因。

上述的TV类可以改写成抽象类:

abstract class TV{      abstract void show();  } 


注意,子类同样需要继承方法,而且要有方法体。

其实电视本来就很抽象,TV本身不需要具体的show方法,具体的电视有具体的实现方法,new TV这个类也没什么用,TV就是一个抽象的概念。

看了一篇英文的回答——What is an abstract class, and when should it be used?

讲的是动物吃东西,动物本身也是一个抽象层次上的概念,但是动物有吃东西的方法,每种不同的动物有自己的吃法,吃肉,吃草,但是总得吃,动物这个概念性的东西,就可以声明为抽象类,如果是普通类,那么子类不重写也可以,那么默认就变成了和动物一样的吃法,但是动物本身并没有吃法,定义为抽象类,抽象方法在子类中需要有实现,这样扩展性也好,这应该就是设计者的初衷。



2)接口

public interface TV {}
不定义public的话,接口只具有包内的访问权限,而接口的方法不声明为public,也是public的,接口可以有域,隐式为static和final。


当然,我们完全可以将TV设计为一个接口。那么为什么有抽象类还要有接口呢?

接口有一个特定的地方,实现接口的类必须实现接口的所有方法,抽象类除外,抽象类可以选择性的实现。这个问题后面总结时解答。


3)完全解耦

耦合性是软件工程的一个概念,程序设计讲究高内聚低耦合,耦合性可以简单看成依赖性,就是粘合度过紧。


书先举了一个例子。

class TV{    public String name(){        return getClass().getSimpleName();//拿到类名    }        Object show(Object input){        return input;    }}class LeTV extends TV{    String show(Object input){        return (String)input+" in letv";    }}class MiTV extends TV{    String show(Object input){        return (String)input+" in mitv";    }}public class TVShow {    public static void play(TV tv , Object o){        System.out.println(tv.name());        System.out.println(tv.show(o));    }    public static void main(String[] args) {        TVShow ts = new TVShow();        String mylove = "my love tv show";        ts.play(new LeTV(), mylove);        ts.play(new MiTV(), mylove);    }}

TVShow的play方法可以根据接收的具体的TV参数而调用不同的方法,这种模式称为—— 策略模式


接下来有Computer,但是专门处理Game。

class Game{    private static int count;    private final int id  = count++;    public String toString(){        return "Game"+id;    }}public class Computer{    public String name(){        return getClass().getSimpleName();//拿到类名    }        Game show(Game input){        return input;    }}class LeComputer extends Computer{    Game show(Game input){        return input;    }}class MiComputer extends Computer{    Game show(Game input){        return input;    }}

虽然书上举的是filter和processor的例子,但是书上这里有一点错误,虽然Computer和TV具有相同的接口元素,但是Computer不是继承TV,根本就是两个类,所以Computer用在TVShow的play方法正常运行肯定是错的。有这个例子也可以看出,TVShow的方法和TV耦合过紧了,只支持TV,要是智能电视呢?


所以,将TV设计为一个接口:

public interface TV {    String name();    Object show(Object input);}public class TVShow {    public static void play(TV tv , Object o){        System.out.println(tv.name());        System.out.println(tv.show(o));    }} public abstract class AbstractTV implements TV{    public String name() {        return getClass().getSimpleName();       }    public abstract String show(Object input);        public static void main(String[] args) {        TVShow ts = new TVShow();        String mylove = "my love tv show";        ts.play(new LeTV(), mylove);        ts.play(new MiTV(), mylove);    }}class LeTV extends AbstractTV{    public String show(Object input){        return (String)input+" in letv";    }}class MiTV extends AbstractTV{    public String show(Object input){        return (String)input+" in mitv";    }}

是不是觉得很麻烦,还要我们自己新建一个AbstractTV去实现TV接口。有什么用呢?看看Game的改变:

class Game{    private static int count;    private final int id  = count++;    public String toString(){        return "Game"+id;    }}class ComputerAdapter implements TV{    private Computer c ;    ComputerAdapter(Computer c){        this.c = c;    }    public String name() {        return c.name();    }    public Game show(Object input) {        return c.show((Game)input);    }    }public class ComputerShow{    public String name(){        return getClass().getSimpleName();//拿到类名    }        Game show(Game input){        return input;    }        public static void main(String[] args) {        Game g = new Game();        TVShow.play(new ComputerAdapter(new LeComputer()),g);        TVShow.play(new ComputerAdapter(new MiComputer()),g);    }}class LeComputer extends Computer{    Game show(Game input){        return input;    }}class MiComputer extends Computer{    Game show(Game input){        return input;    }}class Computer{    public String name(){        return getClass().getSimpleName();//拿到类名    }    Game show(Game input){        return input;    }}

发现没有,原来不能给Computer使用的TVShow的play方法现在可以用了,原因是ComputerAdapter实现了TV接口。


其实这种模式又是另外一种设计模式——适配器模式。ComputerAdapter接收不同的Computer同时实现TV接口,以便后面用于TVShow方法,不然单纯的Computer对象无法作为方法参数,适配器能将你所拥有的接口去产生所需要的接口,即这个例子的接口传入,其实很类似策略模式。ComputerAdapter本身就是代理,你只需要传入Computer对象,而看不到里面的方法实现。


还有一个有趣的地方,虽说是实现接口的所有方法,但是ComputerAdapter和AbstractTV实现接口的show方法时却与不同的类型,原因在Object上,如果TV接口的show方法不是Object类型而是某一具体的对象的话,那么实现方法的时候方法类型就要一致。


这种接口的实现用处就是降低耦合性,TVShow不仅能传入继承AbstractTV的对象,对于其他实现TV接口的对象也是可以使用的。刚开始看可能有点看懂,我看书也看了好久,一大堆东西,但总算搞明白了。


4)多重继承

由于只能继承一个类,但是可以实现多个接口,便拥有多个接口的功能。

public class A extends B implements C,D,E{}


5)接口的域

放在接口的域都是自动为static和final,同时也是public的。其实特性反过来证明也是可以的。练习题也有。

public interface T {    int A=1;}public class Test implements T{    public static void main(String[] args) {        System.out.println(Test.A);        //System.out.println(Test.A++);    }}

静态域才可以通过类名直接访问,final的域不能改变,在java5开始,就有了枚举类型了。


最后,就像书上说的“确定接口是理想选择,因而应该总是选择接口而不是具体的类。”这其实是引诱。

通篇下来,会发现接口真的很抽象,抽象要应需求而用,而不是为了用了用。

这和设计模式是一样的道理。

1 0
原创粉丝点击