今天咱們說內部類 設計模式
keywords :inner-class 將一個類的定義放在另外一個類的定義內部 數組
有了她,咱們能夠把一些邏輯相關的類組織起來並放到一塊兒,並控制位於內部類的可視性。起初咱們徹底會作出內部類是一種代碼的隱藏機制的判斷,但實際上內部類遠非如此,她是如此的優雅,能夠與外部類發生關係,進行通訊。 app
inner-class 看上去是使人感到怪異的,但她的特性依然讓人着迷,並且你必須花費更多的時間去設計以及實踐性的去使用他。然而在大多數時間裏面,對inner-class的需求並不是顯得那麼的明顯,可是一旦你掌握並能夠靈活的使用她,我相信其中的益處也是顯而易見的。那麼,咱們還在等什麼呢? this
如今,咱們將擁有一個內部類:
public class Parcel1{ spa
class Contents{ 設計
private int i=11; code
public int value(){ orm
return i; 對象
} 接口
}
class Destination{
private String label;
Destination(String whereTo){
label=whereTo;
}
String readLabel(){
return readLabel;
}
}
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination();
System.out.println(d.readLabel());
}
public static void man(String[] args){
Parcel1 p =new Parcel1();
p.ship(「Tasmania」);
}
}//Use this code and into your compiler, see the result .
就這段代碼而言,實際上經過嵌套,外部類Parcel1的對象p使用了其內部類Destination 中的readLabel方法 ,可是這樣的用法沒有什麼可以讓咱們驚呼的。
如今,咱們將嘗試更爲奇妙的東西,她將實現這樣一個目的:out-class有一個方法,其將返回一個指向內部類的引用;
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 contents(){
return new Contents();
}
public void ship(String dest){
Contents c = new Contents();
Destination d = new Destination();
System.out.println(d.readLabel());
}
public static void main(String[] args){
Parcel2 p = new Parcel2();
p.ship(「Tasmania」);
Parcel2 q = new Parcel2();
Parcel2.Contents c = q.contents();
Parcel2.Destination d = q.to(「Borneo」);
}
}//Use this code and into your compiler, see the result.
正如咱們所看到的,粗體字代表了須要在一個out-class的非static 方法以外的任意位置使用inner-class中的object的用法。
接下來咱們來研究如下inner-class 和out-class是如何發生關係連接
如今,你是否仍是這樣認爲,inner-class只是一種名字隱藏和組織代碼的模式。你仍然沒有感受到她的奇妙之處。那麼如今請注意下面這句話:當生成一個inner-class的objects時,此object與製造它的enclosing object便發生了關係。這種關係就體如今object能夠access enclosing object 中的全部成員(attribute or field ,and functions),而這卻不須要任何的代價。
看看這段有趣兒的代碼:
interface Selector{
boolean end();
Object current();
void next();
}
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;
}
}
private class SequenceSelector implements Selector {
private int i=0;
public boolean end(){
return i==items.length;
}
public Object current() {
return items[i];
}
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 selector = sequence.selector();
while(!selector.end()){
System.out.println(selector.end() + 「 」);
selector.next();
}
}
}//Use this code and into your compiler, see the result.
Class Sequence 中固定大小的Object數組,是以類的形式包裝起來的。只要還有空間即知足 next<items.length ,便可以調用add()方法以實如今序列的末尾處添加新的object。如今咱們須要使用Sequence這個類中的對象,那麼,不難想象到咱們可使用interface。請注意下面這句話,它有關於「迭代器」的設計模式:要獲取Sequence中的每一個對象,可使用Selector接口。相應的,咱們不難看出,全部實現了Selector接口的類看上去都是實現了這樣一些功能的(function):end()方法能夠檢查序列是否到了末尾,current()方法能夠訪問當前對象,而next()功能則實現了移到序列的下一個對象。由於Selector是一個接口而非抽象類,因此很靈活的,當類SequenceSelector 實現了Selector接口時,它能夠以本身的所喜歡的style去運轉。再看看SequenceSelector這個類,他是個Private的類。也正是經過他,Selector接口中所描繪出的功能,纔可以得以實現。在入口方法中,咱們將Sequence這個包含有一個固定長度的數組的類實例化了,並給它傳遞了一個整數型的參數。這樣,一個定長的數組也就誕生了。接着咱們利用對象包裝器,向裏面添加一些String類型的對象。最後,使用Sequence類的對象sequence的selector方法,獲得一個返回值爲協變返回類型的對象,並將其賦值給Selector接口的實例化selector。如今你還認爲inner-class技術不過如此嗎,若是你還這麼想,那麼再仔細看一下實現了Selector接口的類SequenceSelector中的三個方法,他們都用到了Object,在這裏實際上是一個引用,其實它並不屬於類SequenceSelector,而僅僅是enclosing class 中的一個私有的filed。這樣看來inner-class已經擁有了enclosing class中的全部內容。這是多麼使人難以想象啊,你可能會這樣問,那麼這是如何實現的呢?inner-class 會在適當的時候catch一個指向那個enclosing-class的引用。就這樣,當你用inner-class的對象access那個enclosing-class的內容的以前,會調用那個引用,實際上那個神祕兮兮「引用先生」在訪問enclosing-class的內容。固然,這一切都是託了compiler的福。
關於.this和.new
看到這兩個熟悉的keywords,你必定有不少想說的,他們對咱們來講是顯得那麼的熟悉,但同時也經常會令咱們十分苦惱。
如今想要引用out-class的object,那麼能夠這樣寫:
out-classname.this 這樣作的好處就是咱們能夠自動得到一個正確類型的對象,編譯器會幫助咱們作好審查工做,換回來的固然是運行時的零開銷。
看看這個「.this」是如何apply的:
public class DoThis{
void() f(){
System.out.println(「DoThis.f()」);
}
public class Inner{
public DoThis outer(){
return DoThis.this;
}
}
public Inner inner(){
return new Inner();
}
public static void main(String[] args){
DoThis dt = new DoThis();
DoThis.Inner dti = dt.inner();
dti.outer().f();
}
}Use this code and into your compiler ,see the result
如今你明白了,如何生成對外部類對象的引用,那麼你也許會問,若是想要讓某些小傢伙(enclosing-class's object)去建立在它們本身內部的某個inner-class's object ?
接下來我將展現,上面的問題時如何在代碼中得以實現的
public class DotNew{
public class inner{
//nothing in this class.
}
public static void main(String[] args){
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new inner();
}
}//Use this code and into your compiler ,see the result.
請記住,若是想要去建立一個inner-class's object,那麼應該使用enclosing-class's object 去建立一個inner-class's object ,因此擁有內部來對象的前提是,你必須先建立一個外部類的對象。不過這也有例外,下面看嵌套類,她是一個靜態的內部類,她不須要對外部類對象的引用。
public class Parcel3{
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 static void main(String[] args){
Parcel3 p = new Parcel3();
Parcel3.Contents d = p.new Contents();
Parcel3.Destination d = p.new Destination(「Tasmania」)
}
}Use this code and into your compiler ,see the result.
有關inner-class和up-casting
如今咱們試着將內部類up-casting 爲一個interface,將會發現這個內部類的(其必定是某個接口的實現),可以徹底不可見,而且不可用。咱們獲得的僅僅是指向其parent-class或者interface的reference,因此細節的東西得以實現隱藏。