Java學習筆記-嵌套類,java學習筆記嵌套

來源:互聯網
上載者:User

Java學習筆記-嵌套類,java學習筆記嵌套
嵌套類

嵌套類有兩種類別:static and non-static,分別對應為靜態嵌套類和內部類。

1 class OuterClass {2     ...3     static class StaticNestedClass {4         ...5     }6     class InnerClass {7         ...8     }9 }

 

其中靜態嵌套類只能訪問外部類的靜態成員,內部類可以訪問外部類的任意成員;它們可以被聲明為privatepublicprotected, 或 package private。

  • 靜態嵌套類執行個體化方式為: OuterClass.StaticNestedClass nestedObject = new OuterClass.StaticNestedClass();
  • 內部類執行個體化方式:OuterClass.InnerClass innerObject = outerObject.new InnerClass(); 即通過外部類執行個體才能訪問內部類。

有兩個比較特殊的內部類,分別為局部內部類和匿名類。

局部內部類
  • 局部內部類(Local CLasses)可聲明在類中任意塊(block)中,如方法、for或if塊中
  • 局部內部類可以訪問外部類的成員,若局部內部類聲明在靜態塊中,則可訪問外部類的靜態成員;若聲明在非靜態塊中,則可訪問外部類所有成員;
  • 局部內部類可以訪問所在塊的局部變數,但該局部變數必須聲明為final;在JDK8中進行了改進,局部變數可以聲明為final或effectively final;
  • 其他屬性類別似於普通內部類

其中effectively final與final局部變數的區別在於,前者可以不顯式聲明變數為final,只要在整個過程中,該變數不會被修改(編譯器預設該情況為final)。具體為什麼局部內部類為什麼必須引用final變數,可參考

java為什麼匿名內部類的參數引用時final? 。大致意思是局部內部類引用局部變數,其實是進行的值引用(或者說是值拷貝)。可以認為避免外部代碼塊在內部類運行結束前結束,導致局部變數回收而出錯。

匿名類

匿名類與局部內部類相似,只是沒有命名,並且同時進行聲明和執行個體化。如下:

 1 HelloWorld frenchGreeting = new HelloWorld() { 2             String name = "tout le monde"; 3             public void greet() { 4                 greetSomeone("tout le monde"); 5             } 6             public void greetSomeone(String someone) { 7                 name = someone; 8                 System.out.println("Salut " + name); 9             }10         };

 匿名內部類適用於只用一次的情況。其他的特性與局部內部類相同。

Lambda運算式 在使用匿名內部類的時候,無需提供類名。對於只有一個方法的介面,使用Lambda顯然比匿名類的實現簡單明了。如下所示,定義一個LambdaTest介面,該介面只包含一個opt方法:
1 interface LambdaTest {2     int opt(int a , int b);3 }4 5 LambdaTest sumTest = (a,b) -> a+b;

第5行即為Lambda運算式聲明,其中(a,b)為方法的參數,a+b為方法體,->表示將參數傳遞給方法體。 

  • Lambda運算式的方法體中,可以是一個運算式,也可以是代碼塊。若為運算式,Java運行期會計算運算式,並返回結果;若為代碼塊,可以添加return語句,將結果返回。
  • Lambda運算式其實是一個方法的聲明,可以認為Lambda運算式是匿名方法
  • Lambda運算式與局部內部類和匿名類相似,可以訪問外部類和外部代碼塊的變數;但與後兩者不同,其不存在變數覆蓋的問題,可以認為沒有引入新的代碼塊,其與外部代碼塊中的局部變數同級
  • 由於第三條,所以在運算式的參數中,不能聲明與同級範圍相同的變數名,否則會出現重複定義的異常。
  • Lambda運算式是匿名內部類實現形式的一種,其訪問的外部變數必須是final或effectively final。

舉例如下:

 1 public class Lambda { 2      3     private int var = 100; 4     private String x = "hello"; 5      6     interface Cal{ 7         int op(int a, int b); 8     } 9     10     interface Print{11         void print(String msg);12     }13     14     public int operator(int a, int b, Cal cal) {15         return cal.op(a, b);16     }17 18     public void operator1(String msg, Print print) {19         print.print(msg);20     }21     22     public void operator2(String x) {23         24 //        x = "";25         26         Print print = (msg) -> {27             System.out.println("Lambda訪問外部變數:");28             System.out.println(x);29             System.out.println(msg);30             System.out.println(Lambda.this.x);31         };32         33         print.print(x);34     }35     36     public static void main(String[] args) {37         Cal add = (a,b) -> {return a+b;};38         Cal mul = (a,b) -> a*b;39         40         Lambda lambda = new Lambda();41         System.out.println("2+3="+lambda.operator(2, 3, add));42         System.out.println("2*3="+lambda.operator(2, 3, mul));43         44         lambda.var = 200;45         Print print = (msg) -> {46             System.out.println(msg);47             System.out.println(lambda.var);48         };49         lambda.operator1("Hello World", print);50 51         lambda.operator2("Hello Lambda");52     }53 54 }

運行結果:

1 2+3=52 2*3=63 Hello World4 2005 Lambda訪問外部變數:6 Hello Lambda7 Hello Lambda8 hello

 其中operator2方法可以驗證後三條,如果將24行的注釋取消,28行就會報“local variables referenced from a lambda expression must be final or effectively final”的異常。

目標類型(Target Type)

目標類型為外部類方法期望調用的類型,如上例中operator期望調用的目標方法為Cal。Java會根據Lambda運算式所處的語境和上下文資訊判斷目標類型,並實現調用。

舉例如下:
 1 public class TargetType { 2      3     interface Cal{ 4         String op(); 5     } 6      7     interface Cal1{ 8         int op1(); 9     }10     11     interface Cal2{12         void op1();13     }14     15     public static String invoke(Cal cal) {16         return cal.op();17     }18     19     public static void invoke(Cal1 cal1) {20         cal1.op1();21     }22     23     public static void invoke(Cal2 cal2) {24         cal2.op1();25     }26 27     public static void main(String[] args) {28         invoke(() -> "done");29         invoke(() -> 100);30         invoke(() -> {return;});31     }32 }

聲明三個介面(Cal Cal1 Cal2),具有相同名稱的方法,但他們的傳回值不同。另聲明了3個invoke方法,分別接收3個類,即期望的目標類型不同。然後進行測試:

main方法中的三個語句都通過編譯,並且eclipse提示28行調用目標類型為Cal的invoke,29行調用目標類型為Cal1的invoke,30行調用目標類型為Cal2的invoke,目標類型如所示:

(1)如果再添加一句如:invoke(() -> 100.0);  則編譯器會報錯,Type mismatch: cannot convert from double to String;

(2)如果將Cal介面方法的傳回值改為int,則除了28行報錯,29行也報錯:The method invoke(TargetType.Cal) is ambiguous for the type TargetType,即編譯器無法確定調用哪個目標類型。

 官網文檔中舉的例子為Runnable和Callable,原理一樣,如下:
1 public interface Runnable {2     void run();3 }4 5 public interface Callable<V> {6     V call();7 }

 方法聲明:

1 void invoke(Runnable r) {2     r.run();3 }4 5 <T> T invoke(Callable<T> c) {6     return c.call();7 }

 

 根據上下文確定目標類型,由於有傳回值,所以會調用參數為Callable的invoke方法:
1 String s = invoke(() -> "done");
 總結:
  • 靜態嵌套類與內部類區別
  • 兩類特殊的內部類,局部內部類和匿名內部類;
  • 匿名內部類的特殊實現:Lambda運算式,可認為匿名方法的實現;
  • Lambda運算式會根據上下文環境確定目標類型
參考:
  • Nested Classes
  • Lambda Expressions
 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.