Java編程思想之十 內部類

能夠將一個類定義放在另外一個類的定義內部,這就是內部類。java

10.1 建立內部類

建立內部類就是把類的定義置於外部類裏面。閉包

public class Parcell {
    class contents{
        int i=0;
        public void GetI(){
            System.out.println("contents"+i);
            i++;
        }

    }
    class Destintion{
        private String label;
        public void GetI(String s){
            System.out.println(s);
        }
    }
    public contents GetontentsC(){
        return new contents();
    }
    public void ship(String dest){
        contents c=new contents();
        c.GetI();
        Destintion d=new Destintion();
        d.GetI(dest);
    }

    public static void main(String[] args){
        Parcell p=new Parcell();
        p.ship("dwdwdw");
        Parcell.contents c=p.GetontentsC();
        c.GetI();
    }
}

若是想從外部類的非靜態方法以外的任意位置建立某個內部類的對象,那麼必須具體的指明這個對象的類型:OuterClassName.InnerClassName.app

10.2 連接到外部類

當生成一個內部類對象時,此對象和製造它的外圍對象之間有一種聯繫,全部,它能訪問它外圍對象的全部成員,而不須要特殊條件。框架

public class Sequence {
    private Object[] items;
    private int next=0;
    public Sequence(int size){
        items=new Object[size];
    }
    public void add(Object x){
        if (next<items.length)
            items[next++]=x;
    }
    public class SequenceSelector implements Selector{
private int i=0;
        @Override
        public boolean end() {
            if (i==items.length)
                return false;
            return true;
        }

        @Override
        public Object current() {
            return items[i];
        }

        @Override
        public void next() {
            if (i<items.length)i++;
        }
    }
    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] args){
        Sequence sequence=new Sequence(10);
        for (int i=0;i<10;i++){
            sequence.add(Integer.toString(i));
        }
        Selector s=sequence.selector();
        while (s.end()){
          System.out.print(s.current());
            s.next();
        }
    }
}
interface Selector{
    boolean end();
    Object current();
    void next();
}

當外圍類對象建立一個內部類對象時,此內部類會祕密捕獲一個指向外圍類對象的引用,而後,在你訪問此外圍類成員時,就用那個引用來選擇外圍類成員ide

10.3 使用.this與.new

若是須要生成對外部類對象的引用,可使用外部類的名字後面緊跟.this,這樣產生的引用自動具備正確的類型。this

public class DotThis {
    void f(){
        System.out.println("DotThis.f()");
    }
    public class Inner{
        public DotThis outer(){
            return DotThis.this;
        }
    }
    public Inner inner(){
        return new Inner();
    }
    public static void main(String[] args){
        DotThis d=new DotThis();
        DotThis.Inner in=d.inner();
        in.outer().f();
    }
}

建立某個內部類對象時,須要使用.new。編碼

public class Parcel3 {
    class Contents{
        private int i=11;
        public int value(){return i;}
    }
    public class Destination{
        private String label;
        Destination(String whereTo){
            label=whereTo;
        }
        String readLabel(){
            return label;
        }
    }
    public static void main(String[] args){
        Parcel3 p=new Parcel3();
        Parcel3.Contents c=p.new Contents();
        Parcel3.Destination d=p.new Destination("123");
        System.out.println(d.label);
    }
}
10.4 內部類與向上轉型

當將內部類向上轉型爲基類,尤爲是一個接口的時候,內部類就頗有用了。由於內部類的某個接口的實現是能夠徹底不可見而且不可用的。設計

public class TestParcel {
    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Contents c = p.contents();
        Destination d = p.destination("tasmaina");
    }
}

interface Destination {
    String readLabel();
}

interface Contents {
    int value();
}

class Parcel4 {
    private class PContents implements Contents {
        private int i = 11;

        @Override
        public int value() {
            return i;
        }
    }

    protected class PDestionation implements Destination {
        private String label;

        private PDestionation(String whereTo) {
            label = whereTo;
        }

        @Override
        public String readLabel() {
            return null;
        }
    }

    public Contents contents() {
        return new PContents();
    }

    public Destination destination(String s){
        return new PDestionation(s);
    }
}

PDestination是protected,因此只要Parce4及其子類,還有與Parce4同一個包中的類能訪問PDestination。
private內部類給類設計者提供了一條途徑,經過這種方式,能夠徹底阻止任何依賴於類型的編碼,而且徹底隱藏實現細節。code

10.5 在方法和做用內的內部類

在方法的做用域內,建立一個完整的類對象

public class Parcel5 {
    public Destination destination(String s){
        class PDestination implements Destination{
            private String label;
            private PDestination(String whereTo){
                System.out.println(whereTo);
                label=whereTo;
            }
            @Override
            public String readLabel() {
                return label;
            }
        }
        return new PDestination(s);
    }
    public static void main(String[] args){
        Parcel5 p=new Parcel5();
        Destination d=p.destination("sa");
    }
}

在任意的做用域嵌入一個內部類

public class Parcel6 {
    private void internalTracking1(boolean b) {
        if (b) {
            class TrackingSlip {
                private String id;

                TrackingSlip(String s) {
                    id = s;
                    System.out.println(id);
                }

                String getSlip() {
                    return id;
                }
            }
            TrackingSlip trac=new TrackingSlip("asd");
           // TrackingSlip ts = new TrackingSlip("slip");
            //String s = ts.getSlip();
        }
}
    public void track() {
        internalTracking1(true);
    }
    public static void main(String[] args){
        Parcel6 p=new Parcel6();
        p.track();
    }
}

TrackingSlip類被嵌入在if語句的做用域內,這不是說該類建立是有條件的,它其實與別的類一塊兒編譯過。可是在TrackingSlip的做用域以外,它是不可用的。

10.6 匿名內部類

public class Parcel7 {
    public Contents contents(){
        return new Contents() {
            private  int i=11;
            @Override
            public int value() {
                System.out.println(i);
                return i;
            }
        };
    }
    public static void main(String[] args){
        Parcel7 p7=new Parcel7();
        p7.contents().value();
    }
}

contents()方法將返回值的生成與表示這個返回值的類的定義結合在一塊兒。
經過new表達式返回的引用被自動向上轉型。

public class Parcel7b {
    class ABContents implements Contents{
        private  int i=11;
        @Override
        public int value() {
            System.out.println(i);
            return i;
        }
    }
    public Contents contents(){
        return new ABContents();
    }
    public static void main(String[] args){
        Parcel7b b=new Parcel7b();
        Contents c=b.contents();
        c.value();
    }
}

下面的基類須要一個有參數的構造器

public class Parcel8 {
    public Wrapping wrapping(int i){
        return new Wrapping(i){
        };
    }
    public static void main(String[] args){
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    }
}
class Wrapping{
    public Wrapping(int i){
        System.out.println(i);
    }
}

Wrapping只是一個具備具體實現的普通類,但它仍是被其導出類看成公共"接口"來使用。
在匿名內部類尾部的分號,不是用來標記內部類結束的,它標記的是表達式的結束,只不過這個表達式正好表示匿名類。
若是定義一個匿名內部類,並但願它使用一個外部定義的對象,那麼就要求參數引用是final的。

public class Parcel9 {
    public Destination wrapping(final int i){
        return new Destination(){
            private int label=i;
            @Override
            public String readLabel() {

                System.out.println(label);
                return "i";
            }
        };
    }
    public static void main(String[] args){
        Parcel9 p9=new Parcel9();
        Destination wr=p9.wrapping(4);
        wr.readLabel();
    }
}

在匿名類中不可能有命令構造器,但經過實例初始化,就可以達到爲匿名內部類建立一個構造器的效果。

public class Parcel8 {
    public Wrapping wrapping(int i){
        return new Wrapping(i){
            {System.out.println(i+1);}
        };
    }
    public static void main(String[] args){
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    }
}
class Wrapping{
    public Wrapping(int i){
        System.out.println(i);
    }
}

匿名內部類與正規的繼承相比有些受限,由於匿名類既能夠擴展類,也能夠是實現接口,但不能二者兼備,若是是實現接口,也只能實現一個。

10.6.1 再訪工廠模式

優先使用類而不是接口。若是設計裏面須要某個接口,那麼必須瞭解它。

10.7 嵌套類

若是不須要內部類對象與其外圍對象有聯繫,可使用內部類聲明爲static。這稱爲嵌套類。
普通的內部類對象隱試地保存了一個引用,指向建立它的外圍類對象。當內部類是static的時,就不是這樣的了。
嵌套類意味着:

  • 要建立嵌套類的對象,並不須要其外圍類的對象。
  • 不能從嵌套類的對象訪問非靜態外圍對象。

普通內部類的字段與方法,只能放在類的外部層次上,全部普通的內部類不能有static數據和static字段,也不能包含嵌套類。

public class Parcel11 {
    private static class parceContents implements Contents{

        @Override
        public int value() {
            return 0;
        }
    }
    public static Contents contents(){
        return new parceContents();
    }
    public static void main(){
        Contents c=contents();
    }
}

在一個普通的內部類中,經過一個特殊的this引用能夠連接到其外圍類對象,嵌套類就沒有這個特殊的this引用,這使得它相似一個static方法。

10.7.1 接口內部的類

正常狀況下,不能再接口中放置任何代碼,但嵌套類能夠做爲接口的一部分,放到接口中的任何類都自動是public和static的。

public interface ClassInInterface {
    void howdy();
    class Test implements ClassInInterface{

        @Override
        public void howdy() {
            System.out.println("howdy");
        }

        public static void main(String[] args){
            new Test().howdy();
        }
    }
}

若是想要建立謀和代碼,使得它們能夠被某個接口的全部不一樣實現所公用,那麼使用接口內部的嵌套類會顯得很方便。

10.7.2 從多層嵌套類中訪問外部的成員

一個內部類被嵌套多少層,它都能透明的訪問全部它所嵌入的外圍類對象。

class MNA {
    private void f(){System.out.println("f");}
    class A{
        private void g(){System.out.println("g");}
        public class B{
            void h(){
                g();
                f();
            }
        }
    }
}
public class Multi{
    public static void main(String[] args){
        MNA mna=new MNA();
        MNA.A maa=mna.new A();
        MNA.A.B maab=maa.new B();
        maab.h();
    }
}

.new 語法能參數正確的做用域,全部沒必要再調用構造器時限定類名。

爲何須要內部類

內部類繼承自某個類或實現某個接口,內部類的代碼操做建立它外圍類的對象,全部能夠認爲內部類提供了某種進入外圍類的接口。
每一個內部類都能獨立地繼承自一個實現,全部不管外圍類是否已經繼承了某個實現,對於內部類都沒有影響。
在一個類中,以某種形式實現兩個接口,有兩種選擇:使用單一類,或者使用內部類。
可是對於抽象類或者具體的類,就只能使用內部類實現多重繼承了。

public class MultiImplementation {
    static void takesD(D d) {
    }

    static void takesE(E e) {
    }

    public static void main(String[] args) {
        Z z = new Z();
        takesD(z);
        takesE(z.makeE());
    }
}
class D {
}
abstract class E {
}
class Z extends D {
    E makeE() {
        return new E() {
        };
    }
}
10.8.1 閉包與回調

閉包是一個可調用對象,它記錄了一些信息,這些信息來自於建立它的做用域,經過這個定義,能夠看出內部類是面向對象的閉包,由於它不只包含外圍類對象的信息,還自動擁有一個指向此外圍類對象的引用,在此做用域內,內部類有權操做全部成員,包括private成員。
經過回調,對象可以攜帶一些信息,這些信息容許它在稍後某個時刻調用初始的對象。
經過內部類實現回調:

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.getCallbackReference());
        Caller1.go();
        Caller1.go();
        Caller2.go();
        Caller2.go();
    }
}
interface Incrementable{
    void increment();
}
class Callee1 implements Incrementable{
private int i=0;
    @Override
    public void increment() {
        i++;
        System.out.println(i);
    }
}

class MyIncrement{
    public static int i=1;
    public void increment(){
        System.out.println("Other operation");
    }
    public static void f(MyIncrement mi){
        mi.increment();
    }
}
class Callee2 extends MyIncrement{
    private int i=0;
    public void increment(){
        super.increment();
        i++;
        System.out.println(i);
    }
    private class Closure implements Incrementable{
        @Override
        public void increment() {
            Callee2.this.increment();
        }
    }
    Incrementable getCallbackReference(){
        return new Closure();
    }
}
class Caller{
    private Incrementable callbackReference;
    Caller(Incrementable cbh){
        callbackReference=cbh;
    }
    void go(){callbackReference.increment();}
}
10.8.2 內部類與控制框架

應用程序框架就是被設計用以解決某類特定問題的一個類或一組類。要應用某個應用程序框架,一般是繼承一個或多個類,並覆蓋某些方法。在覆蓋後的方法中,編寫代碼定製應用程序框架提供的通用解決方案,以解決特定的問題。
控制框架是一類特殊的應用程序框架,它用來解決響應事件的需求。主要用來響應事件的系統被稱爲事件驅動系統
首先,接口描述了要控制的事件:

public abstract class Event {
    private long eventTime;
    protected final long delayTime;
    public Event(long delayTime){
        this.delayTime=delayTime;
        start();
    }
    public void start(){
        eventTime=System.nanoTime()+delayTime;
    }
    public boolean ready(){
        return System.nanoTime()>=eventTime;
    }
    public abstract void action();
}

觸發事件的實際控制框架:

import java.util.ArrayList;
import java.util.List;

public class Controller {
    private List<Event> eventList=new ArrayList<Event>();
    public void addEvent(Event c){
        eventList.add(c);
    }
    public void run(){
        while (eventList.size()>0){
            for (Event e:new ArrayList<Event>(eventList)) {
                if (e.ready()){
                    System.out.println(e);
                    e.action();
                    eventList.remove(e);
                }
            }
        }
    }
}

在如今的設計中,咱們還不知道Event到底作了什麼。可是這個設計關鍵就是使變化的事物與不變的事物相互分離。而這就是內部類要作的事情。
內部類容許:

  • 控制框架的完整實現是由單個的類建立,從而使得實現的細節被封裝了起來。內部類用來表示解決問題所必須的各類不一樣的action()。
  • 內部類可以很容易的訪問外圍類的任意成員,因此能夠避免這種實現變得很笨拙。
public class GreenhouseControls extends Controller {
    private boolean light = false;

    public class LightOn extends Event {
        public LightOn(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            light = true;
        }

        public String toString() {
            return "Light is on";
        }
    }

    public class LightOff extends Event {
        public LightOff(long delayTime) {
            super(delayTime);
        }

        @Override
        public void action() {
            light = false;
        }

        public String toString() {
            return "Light is off";
        }
    }

    public class Restart extends Event {
        private Event[] eventList;

        public Restart(long delayTime, Event[] eventList) {
            super(delayTime);
            this.eventList = eventList;
            for (Event e : eventList)
                addEvent(e);
        }

        public void action() {
            for (Event e : eventList) {
                e.start();
                addEvent(e);
            }
            start();
            addEvent(this);
        }

        public String toString() {
            return "Restarting system";
        }
    }

    public static void main(String[] args){
        GreenhouseControls gc=new GreenhouseControls();
        Event[] eventList={
               gc.new LightOn(20000),
               gc.new LightOff(40000)
    };
        //gc.addEvent(gc.new LightOn(20000));
        //gc.addEvent(gc.new LightOff(40000));

        gc.addEvent(gc.new Restart(200000,eventList));
        gc.run();
    }
}

10.9 內部類的繼承

內部類的構造器必須鏈接到指向其外圍類對象的引用,因此在繼承內部類的時候,那個指向外圍類對象的祕密的引用必須被初始化,而在導出類中,再也不存在可鏈接的默認對象。

public class InheritInner extends WithInner.Inner{
    InheritInner(WithInner wi){//必須指向一個外圍對象的引用
        wi.super();
    }
    public static void main(String[] args){
        WithInner wi=new WithInner();
        InheritInner i=new InheritInner(wi);
    }
}
class WithInner{
    class Inner{}
}

10.10 內部類能夠被覆蓋嗎

public class BigEgg extends Egg{
    public class Yolk{
        public Yolk(){
            System.out.println("BigEgg.Yolk()");
        }
    }
    public static void main(String[] args){
        new BigEgg();
    }
}
class Egg{
    private Yolk y;
    protected class Yolk{
        public Yolk(){System.out.println("Egg.Yolk()");}
    }
    public Egg(){
        System.out.println("New Egg()");
        y=new Yolk();
    }
}

Alt text
當繼承了某個外圍類時,內部類並無發生變化,這兩個內部類是徹底獨立的個體,各自在各自的命名空間內。

public class BigEgg2 extends Egg2 {
    public class Yolk extends Egg2.Yolk {
        public Yolk() {
            System.out.println("BigEgg2.Yolk()");
        }

        public void f() {
            System.out.println("BigEgg2.Yolk.f()");
        }
    }

    public BigEgg2() {
        insertYolk(new Yolk());
    }


    public static void main(String[] args) {
        Egg2 e2 = new BigEgg2();
        e2.g();
    }
}

class Egg2 {
    protected class Yolk {
        public Yolk() {
            System.out.println("Egg2.Yolk()");
        }

        public void f() {
            System.out.println("Egg2.Yolk.f()");
        }
    }

    private Yolk y = new Yolk();

    public Egg2() {
        System.out.println("New Egg2()");
    }

    public void insertYolk(Yolk yy) {
        y = yy;
    }

    public void g() {
        y.f();
    }
}

Alt text

10.11 局部內部類

public class LocalInnerClass
{
    private int count=0;
    Counter getCounter(final String name)
    {
        class LocalCounter implements Counter
        {
            public LocalCounter()
            {
                System.out.println("LocalCounter()");
            }
            @Override
            public int next()
            {
                System.out.print(name);
                return count++;
            }
        }
        return new LocalCounter();
    }
    Counter getCounter2(final String name)
    {
        return new Counter()
        {
            {
                System.out.println("Counter()");
            }
            @Override
            public int next()
            {
                System.out.print(name);
                return count++;
            }
        };
    }
    public static void main(String[] args)
    {
        LocalInnerClass lic=new LocalInnerClass();
        Counter c1=lic.getCounter("Local inner "),
                c2=lic.getCounter2("Anonymous inner ");
        for (int i=0;i<5;i++)
            System.out.println(c1.next());
        for (int i=0;i<5;i++)
            System.out.println(c2.next());
    }
}
interface Counter
{
    int next();
}
相關文章
相關標籤/搜索