2014-03-29 166 views
1

我去搜索瞭解如何在Java中使用lambda表達式,但是卻出現了混亂。所以,我的一個匿名類的理解是這樣的:關於匿名類vs匿名內部類的困惑

public class SomeObject { 
    public static void main(String[] args) { 
    ArrayList list = new ArrayList(); 
    list.add(new SomeObject()); 
    } 

} 

我以前看到的術語匿名內部類,但在那個時候,我不知道什麼是普通的匿名類了。我所看到的很多線索和視頻似乎都將匿名內部類稱爲「匿名類」。他們是同義詞嗎?我的匿名內部類的理解是:

public class Rectangle { 
private double length; 
private double width; 
private double perimeter; 

    public void calculatePerimeter() { 
    perimeter = (2*length) +(2*width); 
    } 

    public static void main(String[] args) { 
     Rectangle square = new Rectangle() { 
     public void calculatePerimeter() { 
      perimeter = 4*length; 
     } 
    }; 
    } 

    } 

所以基本上,而不必寫廣場一個子類,然後覆蓋calculatePerimeter()方法,我只是做一個一次性的平方類別,並覆蓋在他們的方法。它是否正確?

所以,匿名內部類與繼承有關。我不理解它的使用。也許,這是因爲我以前從未使用過它們,或者因爲我沒有太多的編程經驗。你可以給我一些例子或解釋它的用處嗎?

更新:當我將匿名內部類的代碼移到IDE中時,我發現有錯誤;顯然,「方形」甚至不會繼承矩形的字段。這不會使它變得更無用嗎?

請問相當於是:

public class Rectangle { 
private double length; 
private double width; 
private double perimeter; 

    public void calculatePerimeter() { 
    perimeter = (2*length) +(2*width); 
    } 
} 


public class Square extends Rectangle { 
    @Override 
    public void calculatePerimeter() { 
     perimeter = 4*getLength(); 
    } 

    public double getLength() { 
    return length; 
    } 



    } 
+1

如果你想讓子類有權訪問字段,請將它們設置爲'protected'而不是'private'。如果一個類的字段爲'private',它就會說「這些字段是我如何實現我的方法的內部細節,所以我不希望任何人把它們弄糟,甚至不需要我的子類,並且可以隨時刪除它們,或者意思可以改變,所以我不希望任何人使用它們,並且根據它們的價值觀,因爲當我的內部方法改變時,它們會後悔。「 – ajb

+0

我沒有看到,謝謝。但即便如此,當我編寫一個新的子類時,它會繼承這些私有實例變量。在匿名內部類的情況下,它沒有。 – Abdul

+1

你說:「當我編寫一個新的子類時,它會繼承私有實例字段」。在技​​術上它確實沒有,但是你是否聲明瞭子類_inside_超類?即使私有實例字段可以在某些條件下被訪問,所以它會使它看起來像字段被繼承,即使它們不是。你可以將代碼發佈到私有實例變量被繼承的地方嗎?這將是有益的。 – ajb

回答

1

第一個非正方形可以訪問Rectangle中的字段。您需要protectedprivate

public class Rectangle { 
    protected double length; 
    protected double width; 
    protected double perimeter; 

    public void calculatePerimeter() { 
     perimeter = (2*length) +(2*width); 
    } 

    public static void main(String[] args) { 
     Rectangle square = new Rectangle() { 
      public void calculatePerimeter() { 
       perimeter = 4*length; 
      } 
     }; 
    } 

} 

紀念他們這裏是內部類,匿名的一些很好的說明和當地

有兩個附加類型內部類的。你可以在方法體內聲明一個內部類。這些類被稱爲本地類。你也可以在方法體內聲明一個內部類而不用命名該類。這些類被稱爲匿名類。

局部類是在一個塊,這是一組平衡的括號之間的零份或多個聲明中定義的類。您通常會在方法的主體中找到定義的本地類。

匿名類讓你讓你的代碼更簡潔。它們使您能夠同時聲明和實例化一個類。除了沒有名字之外,它們就像本地的課程。如果您只需要使用一次本地課程,請使用它們。

我認爲當你在設計一個API 匿名類的相關性來。你可以創建具體的類來爲每個接口/抽象類實現每一點邏輯,但是這會產生大量的依賴關係,你仍然會缺少一些邏輯。匿名類的一個很好的例子是使用謂詞進行過濾。像Google Guava

可以說我有一個List<Integer>,我要過濾的號碼去掉1秒,並返回一個新的列表

public static List<Integer> filter(List<Integer> input) { 
    List<Integer> rtn = new ArrayList<Integer>(); 
    for(Integer i : input) { 
     if(i != 1) rtn.push(i); 
    } 
    return rtn; 
} 

現在可以說,我想篩選出1和2

public static List<Integer> filter(List<Integer> input) { 
    List<Integer> rtn = new ArrayList<Integer>(); 
    for(Integer i : input) { 
     if(i != 1 && i != 2) rtn.push(i); 
    } 
    return rtn; 
} 

現在讓我們說3和5s ...除了謂詞檢查,這個邏輯完全一樣。因此,我們將創建一個接口

interface FilterNumber { 
    public boolean test(Integer i); 
} 

class Filter1s implements FilterNumber { 
    public Filter1s(){}; 
    public boolean test(Integer i) { return i != 1; } 
} 


public static List<Integer> filter(List<Integer> input, FilterNumber filterNumber) { 
    List<Integer> rtn = new ArrayList<Integer>(); 
    for(Integer i : input) { 
     if(filterNumber.test(i)) rtn.push(i); 
    } 
    return rtn; 
} 

filter(list, new Filter1s()); 

正如你可以組合這成爲乏味太見。這將是比較容易的,只允許API的用戶定義他們想要瓶坯邏輯,如果只需要一次只使用一個匿名類

filter(list, new FilterNumber() { 
    @Override 
    public boolean test(Integer i) { 
     return i != 1 && i != 3 && i != 7; 
    } 
}); 

並延伸到lambda表達式,那豈不是連容易採取了所有的臃腫各地i != 1

list.stream().filter(i -> i != 1) 
3

所以我的一個匿名類的理解是這樣的:

public class SomeObject { 
    public static void main(String[] args) { 
    ArrayList list = new ArrayList(); 
    list.add(new SomeObject()); 
    } 
} 

沒有匿名類存在。類SomeObject有一個名字...因此它不是匿名的。實際上,它只是一個普通的(非嵌套的,非內在的,非匿名的)Java類。


我以前看到的術語匿名內部類,但在那個時候,我不知道什麼是普通的匿名類了。

有沒有這樣的事情作爲「常規匿名類」。所有Java匿名類都是「內部」的。

正如JLS說:

「內部類是一個嵌套類,它是不明確或隱含聲明爲static

內部類包括本地(§14.3),匿名(。 §15.9.5)和非靜態成員類(§8.5)。「


所以,匿名內部類與遺傳有關。

匿名內部類涉及繼承,但是這不是什麼讓他們「內部」。往上看。


我說的是「list.add(我說的是‘list.add(新SomeObject());’在這段時間裏,我還以爲你添加到ArrayList對象,被稱爲匿名類,因爲我們沒有命名它。);「。所有這一次,我認爲你添加到ArrayList中的對象被稱爲匿名類,因爲我們沒有給它命名。

你錯了。而對象不是一個類。

new SomeObject()正在創建一個對象,而不是一個類。但這很正常。對象/實例沒有名字......就JLS而言。

現在變量和字段都有名字......但變量不是對象/實例或類。他們是綁定名稱和插槽之間可以容納一個對象的引用(如果這就是類型聲明允許的)。

1 - 除了在java.lang.Class實例的情況下,...,即使這樣的對象不是實際上從理論的角度來看,類/類型。


或者是簡單地稱爲一個匿名對象和我有兩個搞混了?

沒有。對象沒有名字。所有的Java對象都是「匿名的」。這不是一個有用的區別。 (而見上面,我講的變量...)


至於你Rectangle/Square例子,他們什麼都沒有做匿名類,內部類,嵌套類或類似的東西。它們只是使用普通Java繼承的頂級類。 (並不是說我暗示有另一種「非普通」的繼承......)

+0

我的意思是「list.add(new SomeObject());」。所有這一次,我認爲你添加到ArrayList中的對象被稱爲匿名類,因爲我們沒有給它命名。還是它只是一個匿名對象,我有兩個混合起來? – Abdul

+0

@Abdul好吧,它不是一個類,所以它不能是一個匿名類,我想。 –

+0

@DaveNewton - 很好的猜測:-) –

1

要回答後面的評論,「當我寫一個新的子類,它繼承那些私有的實例變量,在匿名內部類的情況下,沒有「。

子類從未「繼承」超類的字段(使用JLS術語)private。但是,無論如何,子類可能能夠引用這些私有字段,具體取決於它們的位置。如果子類在內聲明爲超類,或者它們都嵌套在同一個頂級類中,則子類的方法仍可以訪問該字段;假設您有一個源文件C.java,只有一個類C,private,private字段在C.java中的某個位置仍可從C.java中的大多數其他位置訪問。

然而,這個測試的時候,我發現了一些有趣的細微差別:

class Foo1 {  
    private int bar1; 
    public static class Foo2 extends Foo1 { 
     public void p() { 
      System.out.println(bar1);    // illegal 
      System.out.println(((Foo1)this).bar1); // works 
     } 
    } 
} 

bar1是可見的,即使它是在超類的私人領域;它不是繼承的,但是可以通過告訴編譯器將Foo2對象看作Foo1來訪問它。但僅指bar1本身失敗; Java將此解釋爲嘗試獲取bar1(實例(不是超類)),但Foo2是靜態的,所以沒有包含實例。

注意,如果Foo2被宣佈爲Foo1,第二println將是非法的,因爲現在bar1是完全不可見,因爲它是私有的。這裏的道德是「繼承」和「可見性」(或「訪問」)不是一回事。同樣的事情適用於匿名內部類。如果您在私有實例字段可見的地方使用一個,那麼您可以參考該字段;如果您在私有實例字段不可見的地方使用它,則不能。爲此目的,類聲明的位置比類的類型(嵌套/內部/匿名)更重要。

假設我們帶走static關鍵字,並使它成爲一個內部類:

public class Foo1 { 

    private int bar1; 

    public Foo1(int x) { 
     bar1 = x; 
    } 

    public class Foo2 extends Foo1 { 

     public Foo2(int x) { 
      super(x * 10); 
     } 

     public void show() { 
      System.out.println("bar1 = " + bar1); 
      System.out.println("((Foo1)this).bar1 = " + ((Foo1)this).bar1); 
      System.out.println("Foo1.this.bar1 = " + Foo1.this.bar1); 
     } 
    } 
} 

public class Test64 { 

    public static void main(String[] args) { 
     Foo1 f1 = new Foo1(5); 
     Foo1.Foo2 f2 = f1.new Foo2(6); 
     f2.show(); 
    } 

}  

現在Foo2對象也是一個Foo1;但由於它是一個內部類,所以一個Foo2實例也有一個包含實例不同Foo1對象。當我們創建Foo2,它的用戶一個超類構造函數來設置超bar1到60但是,它也有一個封閉的實例,其bar1爲5 show()顯示此輸出:

bar1 = 5 
((Foo1)this).bar1 = 60 
Foo1.this.bar1 = 5 

所以才bar1本身指到封閉實例中的字段。