java编程思想(读书笔记):8.接口和内隐类

来源:互联网 发布:淘宝购买魔术道具 编辑:程序博客网 时间:2024/04/28 09:41

八.接口和内隐类
接口和内隐类为你系统中的对象提供更为方便的组织和控制方式。
**接口:**interface更胜于抽象类,因为我们能够借以撰写出可被向上转型为多个基本类型的class,从而达到像c++一样的多重继承的变形。
interface可以内含数据成员,但是这些数据成员会自动变为static和final。是的,interface所提供的只是形式,不含实现的细目。
interface所陈述的是:“所有实现出本接口的classes,看起来都应该像这样。”
方法:interface中的方法都是public。所以当你实现某个接口时,必须将继承自该接口的所有方法都声明为public!
代码演示:

package test;public class Test {    static void t(CanFight x){x.fight();}    static void u(CanSwim x){x.swim();}    static void v(CanFly x){x.fly();}    static void w(ActionCharacter x){x.fight();}    public static void main(String[] args) {        Hero h = new Hero();        t(h);        u(h);        v(h);        w(h);    }}interface CanFight{    void fight();}interface CanSwim{    void swim();}interface CanFly{    void fly();}class ActionCharacter{    public void fight(){System.out.println("action fight");}}class Hero extends ActionCharacter implements CanFight,CanFly,CanSwim{    public void swim(){System.out.println("swim");};//实现    public void fly(){System.out.println("fly");};//实现}

注意,interface CanFight及class ActionCharacter中的fight()的标记式是一致的(这样Hero通过继承ActionCharacter的方法,相当于实现了CanFight接口未实现的方法)

接口存在意义?
1.能够被向上转型为多个基本性别
2.让客户端程序员无法产生其对象,并因此确保这只是一个接口。
interface同时赋予你抽象类和接口的好处,因此你的base class可以不带任何函数定义或任何成员变量,那么应该优先考虑用interface。
扩充interface:利用继承,可以轻易将新的函数加至interface中,也可以通过继承extends将多个interface结合为一个新的interface。
产生产量群:因为interface中的所有数据成员都会自动成为static和final,所以能够方便的产生常量群。
定义于interface中的数据成员都是static和final,但不能是blank finals。但可以被非常量表达式初始化。如:

public interface Rand{int rint = (int)(Math.random()*10);}              

嵌套的interface:

package test;import test.A.DImp2;class A{    interface B{//default        void f();    }    public class BImp implements B{//public         public void f(){}    }    private class BImp2 implements B{//private        public void f(){}    }    public interface C{//public        void f();    }    class CImp implements C{//default        public void f(){}    }    private class CImp2 implements C{//private        public void f(){}    }    private interface D{//private        void f();    }    private class DImp implements D{//private        public void f(){}    }    public class DImp2 implements D{//public         public void f(){}    }    public D getD(){        return new DImp2();    }    private D dRef;    public void receiveD(D d){        dRef = d;        dRef.f();    }}interface E{    interface G{        void f();    }    //多余的public    public interface H{        void f();    }    void g();    //错误:成员只能是public    //!private interface I{}}public class Test1 {    public class BImp implements A.B{        public void f(){}    }    class CImp implements A.C{        public void f(){}    }    //不能实现一个private interface,除非在interface被定义的class中    //!class DImp implements A.D{        //public void f(){}    //}    class EImp implements E{        public void g(){}    }    class EGImp implements E.G{        public void f(){}    }    class EImp2 implements E{        public void g(){}        class EG implements E.G{            public void f(){}        }    }    public static void main(String[] args) {        A a = new A();        //A.D not visible        //!A.D ad = a.getD();        A.DImp2 di2 = (DImp2) a.getD();//向下转型        a.receiveD(a.getD());    }}

说明:将interface嵌套在class中,相当直觉。在其中的interface可被定义为正常的可视性。
interface E告诉我们,interface可彼此相互嵌套。但是”所有interface的元素都必须为public“这一条会被严格执行。注意,当你实现某个interface时,你无须实现其中任何嵌套的interface。此外,private interface无法再其所定义的class之外被使用。
内隐类
将某个class的定义置于另一个class定义之中是可行的,这就是所谓的内隐类(inner class)。注意,内隐类和所谓的组合(composition)是截然不同的两回事。
内隐类的典型做法是,外围class有一个函数,可以传回一个reference指向inner class。代码:

package test;public class Parcel2 {    //内隐类    class Contents{        private int i = 11;        public int value(){return i;}        }    class Destination{        private String label;        Destination(String whereTo){            label = whereTo;        }        String readLabel(){            return label;        }    }    public Destination to(String s){        return new Destination(s);    }    public Contents cont(){        return new Contents();    }    public void ship(String dest){        Contents c = cont();        Destination d = to(dest);        System.out.println(d.readLabel());    }    public static void main(String[] args) {        Parcel2 p = new Parcel2();        p.ship("mianyang");        Parcel2 q = new Parcel2();        //给内隐类分配reference        Parcel2.Contents c = q.cont();        Parcel2.Destination d = q.to("tianjin");        Parcel2.Destination d2 = new Parcel2().new Destination("chengdu");        System.out.println(d.readLabel());        System.out.println(d2.readLabel());    }}

输出:

mianyangtianjinchengdu

内隐类和向上转型
当你开始向上转型至base class,尤其是转型为interface,就能凸显inner class的好处(注意:从某个”实现出interface I“的inner class对象身上产生一个reference指向I,本质上和”向上转型至base class“是一样的),这是因为inner class(也就是I的实现者)可以在接下来的情境中完全不被看见,而且不为任何人所用,这么一来我们就很方便能够”隐藏实现细目“。你所得到的只是”指向base class或interface“的一个reference。
一般的(non-inner)classes无法被声明为private或者protected,只能是public或friendly。
inner class的隐晦使用方式
你可以将inner class置于函数之内或者甚至置于程序范畴(scopes)之内。你这样做可能有两个理由:
1.你想实现某种interface,使你得以产生并返回某个reference。
2.你正在解决某个复杂问题,而你希望在解决方案中设计某个class,又不希望这个class被外界所用。
在任意程序范畴嵌套inner class:

package test;public class Parcel5 {    //在任意函数范畴(scope)内嵌套inner class、    private void internalTracking(boolean b){        if(b){            class TrackingSlip{                private String id;                TrackingSlip(String s) {                    id = s;                }                String getSlip(){                    return id;                }            }            TrackingSlip ts = new TrackingSlip("slip");            String s = ts.getSlip();            System.out.println(s);        }        //超出scope,不能够被使用        //!TrackingSlip ts1 = new TrackingSlip("slip");    }    public static void main(String[] args) {        Parcel5 p = new Parcel5();        p.internalTracking(true);    }}

注意:class TrackingSlip被嵌套在if语句中,并不意味这该class会随着条件的成立才被产生,事实上,他会和其他classes一起被编译出来。不过它在它所处的范畴之外就不能被使用。
与外围class的连接关系
截止目前,inner class看起来似乎是一种用于名称隐藏和程序代码组织的体制。这并不能完全让人信服,它还有另外一个作用。当你建立一个inner class时,其对象便拥有了与其制造者–那个外围(enclosing)对象–之间的一种连接关系。所以它可以访问外围对象的所有成员而无需添加任何饰词。此外,inner class也可以访问enclosing class的所有元素。

package test;interface Selector{    boolean end();    Object current();    void next();}public class Sequence {    private Object[] obs;    private int next = 0;    public Sequence(int size){        obs = new Object[size];    }    public void add(Object o){        if(next<obs.length){            obs[next] = o;            next++;        }    }    private class SSelector implements Selector{        int i = 0;        public boolean end(){            return i == obs.length;    }        public Object current(){            return obs[i];            }        public void next(){            if(i<obs.length)                i++;        }        }    public Selector getSelector(){        return new SSelector();    }    public static void main(String[] args) {        Sequence s = new Sequence(10);        for (int i = 0; i < 10; i++)                 s.add(Integer.toString(i));                Selector s1 = s.getSelector();                while(!s1.end()){                    System.out.println(s1.current());                    s1.next();                }    }                    }

sequence只是一个大小固定的Object array,以class形式加以包装。要想取得sequence中的每个对象,有个名为selector的interface,可以让你执行相关操作。由于selector是个interface,所以任何class都可以以自己的形式来实现此一interface,而很多函数都可以接受此一interface作为引数,藉以产生一般化的程序代码。
乍看之下,SSelector只不过是个inner class,但是注意到其函数中都用到了obs,并不是SSelector的一部分,而是外围class的private成员变量。这样就带来了很大的便利。
静态内隐类
如果你不需要inner class对象和enclosing class对象之间的连接关系,你可以将inner class声明为static。一般的inner class(也就是non-static inner class)会自动记录一个reference指向enclosing class的某个对象,而后者也就是此inner class对象的制造者。但是一旦你将inner class声明为static,上述说法不成立。static inner class意味着:
1.产生其对象时,并不需要同时存在一个enclosing class对象。即不需要s.getSelector()中的s
2.你无法在static inner class对象中访问enclosing class对象。
另一方面:non-static inner class内的所有数据和函数都只能位于class的外层(此语只是一种形容,没有实际的技术意义),所以它不能够拥有任何static data,static fields,static inner class。然而static inner class可以拥有这些东西。

package test1;interface Contents{    public int value();    }interface Destination{    public String readLabel();}public class Parcel10 {    private static class pContents implements Contents{        private int i = 11;        public int value(){return i;}    }    protected static class pDestination implements Destination{        private String label;        private  pDestination(String whereTo){            label = whereTo;        }        public String readLabel(){            return label;        }        //static class可以包含static elements        public static void f(){}        static int x = 10;        static class AnotherLevel{            public static void f(){}            static int x = 10;        }    }    public static Destination dest(String s){        return new pDestination(s);    }    public static Contents cont(){        return new pContents();    }    public static void main(String[] args) {        Contents c = cont();        Destination d = dest("tianjin");    }}

main函数中完全不需要Parcel10对象,它只要采用一般用来选择static成员的语法,调用”传回reference(指向Contents)和reference(指向Destination)“的函数即可。
一般而言,你不能将任何程序代码置于interface内,但static inner class却可以是interface中的一部分,这是因为class既然被声明为static,那也就不会破坏interface的规则–static inner class只不过是被置于interface的命名空间中罢了。
non-static inner class之中对于外围class对象的连接关系,是通过一个特殊的this reference形成。
注意两点:
1.在需要产生一个reference指向outer class对象时,命名方式便是在outer class名称之后紧接一个句号,然后再接this。举例来说,class Sequence.SSelector内的任何函数都可以通过Sequence.this来产生一个reference指向Sequence。产生出来的reference会自动被设定正确性别。这样,编译期即可得知确切型别并加以检查,所以不会有执行期的额外负担。(inner class内部产生outer class对象)
2.要产生inner class对象,就需要先产生一个outer class对象,否则无法得到该对象。因此,除非你已经拥有了一个outer class对象,否则便无法产生其inner class对象。这是因为inner class对象会被暗中连接到某个outer class对象上,后者即该inner class对象的制造者。但是对于static inner class,那就不需要一个reference指向outer class对象。(outer class中产生inner class对象)
继承inner class:
由于inner class的构造函数必须连接到一个reference指向outer class对象身上(应该是编译器自动完成),所以当你继承inner class时,事情变得复杂些。问题出在“指向outer calss对象”的那个神秘reference必须被初始化,但derived class之内不存有可连接的缺省对象。这个问题的答案是,使用专用语法,明确产生该关联性。
代码如下:

package test1;class WithInner{    public WithInner(){        System.out.println("withinner");    }    //内部类    class Inner{        public Inner(){            System.out.println("inner");        }    }}public class InheritInner extends WithInner.Inner{    //不能够被编译    //!public InheritInner() {    //}    InheritInner(WithInner wi){        wi.super();        System.out.println("inheritinner end");    }    public static void main(String[] args) {        WithInner wi = new WithInner();        InheritInner ii = new InheritInner(wi);    }}

结果如下:

withinnerinnerinheritinner end

InheritInner继承的是inner class而非outer class,但是当编译至构造函数时,default构造函数有问题;而且你也不能够只是传入一个reference指向outer object,你还必须在构造函数中使用一下语法:
enclosingClassReference.super();这么一来便能提供所需的reference,而程序也能顺利编译下去。
如果删除:

InheritInner(WithInner wi){        wi.super();        System.out.println("inheritinner end");    }

在new InheritInner(wi);处会报错,显示:The constructor InheritInner(WithInner) is undefined。

为什么需要inner class?
一般来说,inner class会继承某个class或实现某个interface,而且inner class内的程序代码会操作其outer class对象。可以这样说,inner class所提供的其实是针对outer class的某种窗口。
有个问题直指inner class的核心:如果我只需要”指向某个interface“的reference,为什么我不直接让outer class实现该interface呢?答案是,如果这么做能符合你的需求,你确实应该这么做。那么,”由inner class实现interface“和”由outer class实现interface“两者之间的区别究竟在哪儿?答案是后者将无法总是享受到interface的便利性–有时候你得下探实现细目。关于inner class的存在,最信服的理由就是:
每个inner class都能够各自继承某一实现类。因此,inner class不受限于outer class是否已继承自某一实现类。
从另一个角度看,它是多重继承问题的完美解决方案。interface能够解决其中一部分问题,但inner class才能实际允许你继承自多个实现类。如果你拥有的不是interface,而是抽象或实在的class,你就必须使用inner class来解决”多重继承“的问题。
通过inner class,你可以拥有下列几个额外性质:
1.inner class可以拥有多分实体(instance),每个实体都拥有专属的状态信息,而这些信息和outer class对象的信息是相互独立的。
2.在单一outer class内你可以拥有多个inner class,每个都实现相同的interface,或以不同方式继承同一个class。(???)
3.产生inner class对象的时间点,不见得必须和产生outer class对象同时。
4.outer class和inner class之间不存在is-a的关系,inner class就是一个独立的个体。
举个例子,如果Sequence.java不使用inner class,那么你就得宣称”Sequence是个Selector“,而且对特定某个Sequence而言,你只能拥有单一的Selector。另外,如果你希望拥有第二个函数,getRSelector(),令它产生一个“回头走”的Selector,那么你必须采用inner class,才能有如此弹性。
Closures(终结)和callbacks(回调)
所谓closure是一种可被调用的对象,他会记录一些信息,这些信息来自它的产生地所在的程序范畴(scope)。在callback机制底下,某个对象被赋予一些信息,这些信息允许该对象在稍后某个时间点上调用原先的对象。让inner class提供closure功能,是完美解决方案,比起指针来说,不但更具弹性,而且安全许多。代码示例如下:

package test1;//用inner class实现callbacksinterface Incrementable{    void increment();}//简单实现接口class Callee1 implements Incrementable{    private int i = 0;    public void increment(){        i++;        System.out.println(i);    }}class MyIncrement{    public void increment(){        System.out.println("other operation");    }    public static void f(MyIncrement mi){        mi.increment();    }}//如果你的class要以其他方式实现increment(),你应该用inner classclass Callee2 extends MyIncrement{    private int i =0;    private void incr(){        i++;        System.out.println(i);    }    //inner class    private class Closure implements Incrementable{        public void increment(){            incr();        }    }    Incrementable getCallbacksReference(){        return new Closure();    }}class Caller{    private Incrementable callbackReference;    Caller(Incrementable cbh){        callbackReference = cbh;    }    void go(){        callbackReference.increment();    }}public class Callbacks {    public static void main(String[] args) {        Callee1 c1 = new  Callee1();        Callee2 c2 = new Callee2();        MyIncrement.f(c2);        Caller caller1 = new Caller(c1);        Caller caller2 = new Caller(c2.getCallbacksReference());        caller1.go();        caller1.go();        caller2.go();        caller2.go();        for (int i = 0; i < 10; i++) {            caller1.go();        }    }}

结果:

other operation12123456789101112

就程序代码而言,Callee1无疑是较简单的方法,Callee2继承自MyIncrement,后者拥有另一个不同的increment(),这个函数会执行一些动作,而这些动作和Incrementable interface预期应该要做的事毫无关联。当MyIncrement被Callee2继承,你无法重写increment()以为Incrementable所用。所以你得利用inner class另行提供一份独立的实现码。请注意,当你撰写inner class时,你并不会将任何东西加入outer class的接口,或修改该接口。
inner class很单纯地藉由“实现Incrementable”来提供与Callee2之间的关联。这个关联很安全。
Caller于其构造函数中接受Incrementable reference作为引数,而且在某段时间之后,它会使用这个reference来“回头调用”Callee class。
callback的价值在于其弹性–你可以在执行时期动态决定究竟调用哪个函数。

0 0
原创粉丝点击