Java編程思想:第10章 內部類

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

1.能夠實現隱藏閉包

2.內部類瞭解外圍類,並能與之通訊,不少時候能夠寫出更加優雅和清晰的代碼框架

10.1建立內部類this

public class Outer{spa

  class Inner{設計

  }code

}對象

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

10.2連接到外部類接口

內部類自動擁有對外圍類全部成員的訪問權,這是如何作到的呢?當外圍類對象建立了一個內部類對象時,此內部類對象一定會祕密的捕獲一個指向那個外圍類對象的引用,在訪問外部類對象成員時,就是用這個引用來訪問。編譯器會幫助咱們處理這些細節,而咱們使用時,就像是內部類自己帶有的同樣。因此非static內部類在建立時候必需要由外部類對象來建立。

10.3使用.this與.new

若是要使用外部類對象的引用,OuterClassName.this來獲取,編譯器會檢查類型是否正確,無運行時開銷。

建立內部類對象:要先有外部類對象Outer o = new Outer();而後用外部類對象建立內部類對象Outer.Inner i = o.new Inner();不能夠用o.new Outer.Inner();

10.4內部類向上轉型

當把private內部類向上轉型成基類,尤爲是接口的時候,內部類就有了用武之地:這個接口的實現能夠徹底隱藏,徹底不可見,由於返回的是接口引用

參考java源碼AbstractList類:

public Iterator<E> iterator() {

  return new Itr();

}

private class Itr implements Iterator<E> {

  ...

}

當咱們外界使用迭代器的時候

Iterator iter = arrayList.iterator();

只能拿到接口的引用,徹底不知道里面是一個private內部類,由於Itr是private的,也不能建立它。

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

局部內部類:定義在方法體或者某一做用域裏,超出做用域不能使用。

注意:不能使用意味着在外部不能被訪問到類型,可是該類型在JVM裏是實際存在的,即便這個做用域已經結束。

10.6匿名內部類

沒有名字的內部類,能夠有帶參構造器,能夠添加並初始化域。

若是在內部類的內部要使用外部的一個對象,這個對象的引用須要是final的。構造器中使用的參數不須要是final的。

匿名內部類裏不能本身添加構造器(由於不能被訪問到,並且沒名字),須要作一些初始化的時候,能夠用代碼塊來實現。

匿名內部類與正規的繼承相比,要麼擴展類,要麼實現接口,不能兩者都有,而且只能實現一個接口。

10.6.1再訪工廠方法

能夠把工廠方法改爲匿名內部類。

interface Game{boolean move();}

interface GameFactory{Game getGame();}

class Checkers implements Game{

  private Checkers(){}

  private int moves = 0;

  private static final int MOVES = 3;

  public static GameFactory factory = new GameFactory(){

    public Game getGame(){return new Checkers();}  

  };

  public boolean move(){

    return ++moves != MOVES;

  }

}

10.7嵌套類-靜態內部類

1)建立靜態內部類對象不須要外部類對象

2)不能從靜態內部類對象訪問外圍內的非靜態成員

普通內部類不能夠有static字段,靜態能夠有

10.7.1接口內部的類

正常狀況下,接口內部是不能聽任何代碼的,可是嵌套類能夠放。放入接口中的任何類都是public static的。

用途:若是你想建立某些公共代碼,使得它們能夠被這個接口的全部實現使用,那麼接口中嵌套內部類就會很是方便。

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

一個內部類被嵌套多少層並不重要,它能夠透明的訪問全部外圍類的全部成員。

class MMA{

  private void f(){}

  class A{

    private void g(){}

    public class B{

      void h(){

        f();//直接調用 沒有問題

        g();

      }

    }

  }

}

10.8爲何須要內部類

通常來講,內部類繼承某個類或實現某個接口,而且能夠操做外圍類對象。

核心緣由:內部類能夠提供多重繼承的能力。

好比: AbstractList裏有Itr和ListItr兩種迭代器,若是咱們只是讓AbstractList實現Iterator接口,那麼只能寫出一個迭代器,經過使用內部類方式能夠添加多個迭代器。

10.8.1閉包與回調

若是有一個類須要實現一個接口來提供某個方法,可是發現這個方法已經被另一個接口使用了,那麼能夠用內部類代替這個類去實現接口。

回調的價值在於它的靈活性---能夠在運行時動態決定須要調用什麼方法。

10.8.2內部類與控制框架

設計一個控制框架,要求到了設定時間就執行相應的事件:

//建立一個抽象事件類,有執行時間和延遲時間,在產生新事件的時候用延遲時間計算出執行時間,action方法由實現類實現。

public abstract class Event{

  private long eventTime;

  protected final long delayTime;

  public Event(long delay){

    this.delayTime = delay;

    start();  

  }

  public void start(){

    eventTime = System.nanoTime() + delayTime;

  }

  public boolean ready(){

    return System.nanoTime >= eventTime;

  }

  public abstract void action();

}

//建立一個控制類,循環遍歷全部事件,若是事件時間到了就執行action

public class Controller{

  private List<Event> eventList = new ArrayList<>();

  public void addEvent(Event e){eventList.add(e);}

  public void run(){

    while(eventlist.size()>0){

      for(Event e : eventList){

        if(e.ready){

          e.action();

          eventlist.remove();

        }

      }

    }

  }

}

從上面這2個類的設計來看,咱們徹底不知道event具體作什麼,可是咱們整個控制框架的核心功能:"把變化的事物(event具體實現),與不變的事物(時間發生過程)"分開。而咱們只須要建立不一樣的變化的Event,這正是內部類要作的事情:

1)控制框架裏的實現是由單個類建立的,從而使得實現細節被封裝起來。內部類用來表示解決問題所必須的不一樣action。

2)內部類能夠很容易訪問外部類成員,這種實現不會顯得很笨拙。

控制框架:

public class GreenhouseControls extend Controller{

  private boolean light = false;

  public class LightOn extends Event{

    public LightOn(long delay){super(delay);}

    public void action(){

      //hardware control code to turn on light

      light = true;

    }

  }

  public class LightOff extends Event{..}

  private boolean water = false;

  public class WaterOn extends Event{

    public WaterOn(long delay){super(delay);}

    public void action(){

      //..

      water = true;

    }

  }

  //...其餘內部類

}

控制框架中用內部類把實現細節都隱藏起來了。

那麼如何使用這個框架並執行須要的事件呢:

public static void main(String[] args){

  GreenhouseControls gc = new GreenhouseControls();

  gc.addEvent(gc.new LightOn(100));

  gc.addEvent(gc.new LightOff(200));

  ...

  gc.run();

}

控制框架能夠體現出內部類的價值,圖形界面設計裏更是讓人信服。

10.9內部類的繼承

 內部類的構造器必須鏈接到外圍類對象的引用,若是一個類繼承了內部類,那麼如何保證外圍類對象存在呢?須要使用特殊語法:

1.在導出類添加一個帶外圍類參數的構造器

2.構造器裏用outer.super()來提供外圍類引用

例如:

class Outer{

  class Inner{}

}

public class InheritInner extends Outer.Inner{

  InheritInner(Outer o){

    o.super();

  }

}

10.10內部類能夠被覆蓋嗎

class Outer{

  class Inner{}

}

class Outer2 extends Outer{

  class Inner{}

}

在Outer裏調用Inner並不會被Outer2裏的Inner覆蓋,即不會產生多態特性。

10.11局部內部類

在方法體裏使用局部內部類和使用匿名內部類有什麼區別呢?

惟一的區別在於:局部內部類能夠有命名的構造器,而且能夠被重載用來初始化,能夠產生多個對象。而匿名內部類只能提供一個該類型的對象。

10.12內部類標識符

匿名內部類: Outer類名 $ 數字

成員內部類:  Outer類名 $ 內部類名

局部內部類: Outer類名 $ 數字 內部類名

10.13總結

接口和內部類實現了C++的多重繼承功能,可是更優雅,更簡潔。

相關文章
相關標籤/搜索