[譯] Android 的 Java 8 支持

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本文原文出自 ","attrs":{}},{"type":"link","attrs":{"href":"https://jakewharton.com/","title":"","type":null},"content":[{"type":"text","text":"jakewharton","attrs":{}}]},{"type":"text","text":" 關於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"R8","attrs":{}}],"attrs":{}},{"type":"text","text":" 系列文章第一篇。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文鏈接 : ","attrs":{}},{"type":"link","attrs":{"href":"https://jakewharton.com/androids-java-8-support/","title":"","type":null},"content":[{"type":"text","text":"Android's Java 8 Support","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"原文作者 : ","attrs":{}},{"type":"link","attrs":{"href":"https://jakewharton.com/","title":"","type":null},"content":[{"type":"text","text":"jakewharton","attrs":{}}]}]}]},{"type":"listitem","attrs":{"listStyle":null},"content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"譯者 : ","attrs":{}},{"type":"link","attrs":{"href":"https://juejin.cn/user/2172290705131016","title":"","type":null},"content":[{"type":"text","text":"Antway","attrs":{}}]}]}]}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我在家辦公已經有幾年了,在此期間,我聽到周圍的人抱怨 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 不同版本的支持力度。在每年的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Google I/O","attrs":{}}],"attrs":{}},{"type":"text","text":" 大會上,你都會發現我針對這個問題在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"fireside chats","attrs":{}}],"attrs":{}},{"type":"text","text":" 環節提問或直接問負責人。但是這是一個複雜的話題,因爲討論 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 能支持到什麼程度我們也不清楚,每一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本中涉及到:語言特性(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"the language features","attrs":{}}],"attrs":{}},{"type":"text","text":")、字節碼(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"the bytecode","attrs":{}}],"attrs":{}},{"type":"text","text":")、工具(","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"the tools","attrs":{}}],"attrs":{}},{"type":"text","text":")、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"APIs","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"JVM","attrs":{}}],"attrs":{}},{"type":"text","text":" 以及其它方面。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當人們談論起 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 的支持通常指的是語言特性,所以接下來讓我們一起開始看看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 的工具鏈是如何處理支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言特性的。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"1. Lambda","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 中最大的語言特性變動是增加了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Lambda","attrs":{}}],"attrs":{}},{"type":"text","text":",相比以前使用更冗長的構造(如匿名類),","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 帶來了一個更簡潔的代碼格式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Java8 {\n interface Logger {\n void log(String s);\n }\n\n public static void main(String... args) {\n sayHi(s -> System.out.println(s));\n }\n\n private static void sayHi(Logger logger) {\n logger.log(\"Hello!\");\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javac","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令編譯爲字節碼後,然後通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dx","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具編譯打包爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dex","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件,但是出錯了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ javac *.java\n\n$ ls\nJava8.java Java8.class Java8$Logger.class\n\n$ $ANDROID_HOME/build-tools/28.0.2/dx --dex --output . *.class\nUncaught translation error: com.android.dx.cf.code.SimException:\n ERROR in Java8.main:([Ljava/lang/String;)V:\n invalid opcode ba - invokedynamic requires --min-sdk-version >= 26\n (currently 13)\n1 error; aborting\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是因爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 使用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokedynamic","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼指令,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokedynamic","attrs":{}}],"attrs":{}},{"type":"text","text":" 是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 7","attrs":{}}],"attrs":{}},{"type":"text","text":" 中引入的。上面的錯誤信息提示,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 支持這種字節碼的最低版本是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"26","attrs":{}}],"attrs":{}},{"type":"text","text":"。與此同時 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"desugaring","attrs":{}}],"attrs":{}},{"type":"text","text":"(脫糖)兼容所有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本上使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"2. Desugaring(脫糖)的歷史","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"脫糖工具的發展史非常出彩,但是它的核心目標卻是一致的:讓所有的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言新特性都能運行在所有設備上。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://github.com/luontola/retrolambda","title":"","type":null},"content":[{"type":"text","text":"Retrolambda","attrs":{}}]},{"type":"text","text":" 是最初支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式的第三方工具庫,它通過在編譯時利用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"JVM","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令將 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 轉換爲內部類來實現。然而生成的類會使方法數激增,但是隨着時間的推移,使用該工具的成本降低到了合理的水平。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"然後,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具團隊宣佈了一個新的編譯器,它將提供 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言特性的支持,以及更好的性能。該工具是建立在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Eclipse Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 編譯器上的,而不是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Dalvik Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼之上的。雖然處理 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 效率很高,但是它的體驗很差以及無法與別的工具兼容。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終新的編譯器被捨棄,同時在 ","attrs":{}},{"type":"link","attrs":{"href":"https://android-developers.googleblog.com/2017/04/java-8-language-features-support-update.html","title":"","type":null},"content":[{"type":"text","text":"Android Gradle plugin","attrs":{}}]},{"type":"text","text":" 中引入了谷歌定製的字節碼構建系統,因爲脫糖是增量式的,所以脫糖的輸出效率仍然不是很理想,與此同時,正在進行的工作有了更好的方案。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"https://android-developers.googleblog.com/2017/08/next-generation-dex-compiler-now-in.html","title":"","type":null},"content":[{"type":"text","text":"D8","attrs":{}}]},{"type":"text","text":" 編譯工具問世了。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 編譯工具用來替代老的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dx","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具,同時在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 中集成了脫糖,以此取代脫糖作爲一個獨立的字節碼轉換模塊的方式。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 相比較 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dx","attrs":{}}],"attrs":{}},{"type":"text","text":" 有很大的提升,帶來了更有效率的字節碼轉換。同時在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android Gradle Plugin 3.1","attrs":{}}],"attrs":{}},{"type":"text","text":" 中作爲默認 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dex","attrs":{}}],"attrs":{}},{"type":"text","text":" 編譯器,然後在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"3.2","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本中 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 又集成了脫糖。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"3. D8","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具編譯上面的例子成功了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ java -jar d8.jar \\\n --lib $ANDROID_HOME/platforms/android-28/android.jar \\\n --release \\\n --output . \\\n *.class\n\n$ ls\nJava8.java Java8.class Java8$Logger.class classes.dex\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同時我們可以通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 提供的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dexdump","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具來查看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dex","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件內容,看看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 是如何脫糖的,由於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dexdump","attrs":{}}],"attrs":{}},{"type":"text","text":" 會產生很多代碼,我們只截取一部分。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ $ANDROID_HOME/build-tools/28.0.2/dexdump -d classes.dex\n[0002d8] Java8.main:([Ljava/lang/String;)V\n0000: sget-object v0, LJava8$1;.INSTANCE:LJava8$1;\n0002: invoke-static {v0}, LJava8;.sayHi:(LJava8$Logger;)V\n0005: return-void\n\n[0002a8] Java8.sayHi:(LJava8$Logger;)V\n0000: const-string v0, \"Hello\"\n0002: invoke-interface {v1, v0}, LJava8$Logger;.log:(Ljava/lang/String;)V\n0005: return-void\n…\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"main","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法中,對應 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"0000","attrs":{}}],"attrs":{}},{"type":"text","text":" 位置創建了一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 類對象 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"INSTANCE","attrs":{}}],"attrs":{}},{"type":"text","text":" 實例,但是我們的源文件中並不包含這個類,所以猜測這個類是由脫糖產生的。同時 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"main","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的字節碼中也沒有包含任何 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現,所以很可能是在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 中實現的。在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"0002","attrs":{}}],"attrs":{}},{"type":"text","text":" 位置,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"INSTANCE","attrs":{}}],"attrs":{}},{"type":"text","text":" 調用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sayHi","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法,同時可以看到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sayHi","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的參數是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LJava8$Logger","attrs":{}}],"attrs":{}},{"type":"text","text":" ,所以基本可以確定 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 類實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 中的接口。我們可以輸出字節碼進行驗證。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"Class #2 -\n Class descriptor : 'LJava8$1;'\n Access flags : 0x1011 (PUBLIC FINAL SYNTHETIC)\n Superclass : 'Ljava/lang/Object;'\n Interfaces -\n #0 : 'LJava8$Logger;'\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"SYNTHETIC","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼標籤代表着這個類是由系統產生,通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Interfaces","attrs":{}}],"attrs":{}},{"type":"text","text":" 可以看到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LJava8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 類實現了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LJava8$Logger","attrs":{}}],"attrs":{}},{"type":"text","text":" 接口。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"現在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LJava8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現已經替代了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":",我們可以通過查看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sayHi","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法的字節碼實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"…\n[00026c] Java8$1.log:(Ljava/lang/String;)V\n0000: invoke-static {v1}, LJava8;.lambda$main$0:(Ljava/lang/String;)V\n0003: return-void\n…\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"sayHi","attrs":{}}],"attrs":{}},{"type":"text","text":" 的字節碼實現中,它調用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8","attrs":{}}],"attrs":{}},{"type":"text","text":" 類中的靜態方法 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda$main$0","attrs":{}}],"attrs":{}},{"type":"text","text":",但是我們並沒有在類中定義這個方法,所以我們只能查看下 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8","attrs":{}}],"attrs":{}},{"type":"text","text":" 類對應的字節碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"…\n #1 : (in LJava8;)\n name : 'lambda$main$0'\n type : '(Ljava/lang/String;)V'\n access : 0x1008 (STATIC SYNTHETIC)\n[0002a0] Java8.lambda$main$0:(Ljava/lang/String;)V\n0000: sget-object v0, Ljava/lang/System;.out:Ljava/io/PrintStream;\n0002: invoke-virtual {v0, v1}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V\n0005: return-void\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在這裏我們通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"SYNTHETIC","attrs":{}}],"attrs":{}},{"type":"text","text":" 標籤可以確定 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda$main$0","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法是由系統自動生成的,並且看到了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 實現的方法體 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.out.println","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過上面的流程分析,我們可以推測出:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現保持在原來的主類中,並且是私有的,別的類無法直接訪問。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"4. Source Transformation(源碼模擬實現)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了更好的理解脫糖是如何工作的,我們可以在源碼的層面模擬實現,注意這裏的模擬僅僅是爲了加深理解,脫糖實際工作比這個要複雜的多。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Java8 {\n interface Logger {\n void log(String s);\n }\n\n public static void main(String... args) {\n sayHi(s -> System.out.println(s));\n }\n\n private static void sayHi(Logger logger) {\n logger.log(\"Hello!\");\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第一步","attrs":{}},{"type":"text","text":"將 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式移到同級的包私有方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public static void main(String... args) {\n- sayHi(s -> System.out.println(s));\n+ sayHi(s -> lambda$main$0(s));\n }\n+\n+ static void lambda$main$0(String s) {\n+ System.out.println(s);\n+ }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"第二步","attrs":{}},{"type":"text","text":"生成一個內部類實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Logger","attrs":{}}],"attrs":{}},{"type":"text","text":" 接口,並且它的方法體調用剛纔實現的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public static void main(String... args) {\n- sayHi(s -> lambda$main$0(s));\n+ sayHi(new Java8$1());\n }\n@@\n }\n+\n+class Java8$1 implements Java8.Logger {\n+ @Override public void log(String s) {\n+ Java8.lambda$main$0(s);\n+ }\n+}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"最後","attrs":{}},{"type":"text","text":",因爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法並沒有依賴外部的任何類,所以我們在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 內部創建一個單例對象來避免每次調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法都生成一個新對象。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public static void main(String... args) {\n- sayHi(new Java8$1());\n+ sayHi(Java8$1.INSTANCE);\n }\n@@\n class Java8$1 implements Java8.Logger {\n+ static final Java8$1 INSTANCE = new Java8$1();\n+\n @Override public void log(String s) {\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最終我們經過脫糖生成的文件適用與所有 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"APIs","attrs":{}}],"attrs":{}},{"type":"text","text":" 。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"class Java8 {\n interface Logger {\n void log(String s);\n }\n\n public static void main(String... args) {\n sayHi(Java8$1.INSTANCE);\n }\n\n static void lambda$main$0(String s) {\n System.out.println(s);\n }\n\n private static void sayHi(Logger logger) {\n logger.log(\"Hello!\");\n }\n}\n\nclass Java8$1 implements Java8.Logger {\n static final Java8$1 INSTANCE = new Java8$1();\n\n @Override public void log(String s) {\n Java8.lambda$main$0(s);\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際上你在查看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式生成的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Dalvik","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼時可能看到不是類似 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":" Java8$1","attrs":{}}],"attrs":{}},{"type":"text","text":" 的名稱,而是像這樣的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"-$$Lambda$Java8$QkyWJ8jlAksLjYziID4cZLvHwoY","attrs":{}}],"attrs":{}},{"type":"text","text":" 名稱,這是由於命名規範不恰當引起的。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"5. Native Lambdas","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面我們通過 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dx","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具編譯 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dex","attrs":{}}],"attrs":{}},{"type":"text","text":" 文件時,錯誤信息提示我們最低的支持版本是 API 26。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ $ANDROID_HOME/build-tools/28.0.2/dx --dex --output . *.class\nUncaught translation error: com.android.dx.cf.code.SimException:\n ERROR in Java8.main:([Ljava/lang/String;)V:\n invalid opcode ba - invokedynamic requires --min-sdk-version >= 26\n (currently 13)\n1 error; aborting\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以如果我們在使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 的時候指定 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"--min-api 26","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本,應該就不會報錯了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ java -jar d8.jar \\\n --lib $ANDROID_HOME/platforms/android-28/android.jar \\\n --release \\\n --min-api 26 \\\n --output . \\\n *.class\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣爲了查看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 如何工作,我們還是查看 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8","attrs":{}}],"attrs":{}},{"type":"text","text":" 類的字節碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"$ javap -v Java8.class\nclass Java8 {\n public static void main(java.lang.String...);\n Code:\n 0: invokedynamic #2, 0 // InvokeDynamic #0:log:()LJava8$Logger;\n 5: invokestatic #3 // Method sayHi:(LJava8$Logger;)V\n 8: return\n}\n…\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"爲了閱讀方便我只截取了部分代碼,但是我們同樣可以在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"main","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法中看到這裏使用了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"InvokeDynamic","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令,在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Code","attrs":{}}],"attrs":{}},{"type":"text","text":" 表的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"0","attrs":{}}],"attrs":{}},{"type":"text","text":" 位置上,我們可以看到第二個參數是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"0","attrs":{}}],"attrs":{}},{"type":"text","text":",對應着 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"bootstrap method(引導方法)","attrs":{}}],"attrs":{}},{"type":"text","text":"。 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"bootstrap method","attrs":{}}],"attrs":{}},{"type":"text","marks":[{"type":"italic","attrs":{}}],"text":"(引導方法)是當字節碼第一次執行時首先被執行的一小段代碼。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"…\nBootstrapMethods:\n 0: #27 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(\n Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;\n Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;\n Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)\n Ljava/lang/invoke/CallSite;\n Method arguments:\n #28 (Ljava/lang/String;)V\n #29 invokestatic Java8.lambda$main$0:(Ljava/lang/String;)V\n #28 (Ljava/lang/String;)V\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在上面的代碼中,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"bootstrap method","attrs":{}}],"attrs":{}},{"type":"text","text":"(引導方法)對應的是 ","attrs":{}},{"type":"link","attrs":{"href":"https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/LambdaMetafactory.html","title":"","type":null},"content":[{"type":"text","text":"java.lang.invoke.LambdaMetafactory","attrs":{}}]},{"type":"text","text":" 類中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"metafactory","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LambdaMetafactory","attrs":{}}],"attrs":{}},{"type":"text","text":" 類在運行時爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式生成匿名類,而 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 是在編譯時生成。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們查看 ","attrs":{}},{"type":"link","attrs":{"href":"https://developer.android.com/reference/java/lang/invoke/package-summary","title":"","type":null},"content":[{"type":"text","text":"Android documentation for java.lang.invoke ","attrs":{}}]},{"type":"text","text":" 和 ","attrs":{}},{"type":"link","attrs":{"href":"https://android.googlesource.com/platform/libcore/+/master/ojluni/src/main/java/java/lang/invoke/","title":"","type":null},"content":[{"type":"text","text":"AOSP source code for java.lang.invoke","attrs":{}}]},{"type":"text","text":" 的文檔,我們可以注意到這個類在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android Runtime","attrs":{}}],"attrs":{}},{"type":"text","text":" 中不存在,這也是爲什麼脫糖在編譯時要求最小版本的原因。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"VM","attrs":{}}],"attrs":{}},{"type":"text","text":" 環境支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"invokedynamic","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令,但是 JDK 在編譯 LambdaMetafactory 中卻不可用。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"6. Method References(方法引用)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式,方法引用也是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 的語言特性,當 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 的實現是一個已經存在的方法,此時使用方法引用會很方便。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public static void main(String... args) {\n- sayHi(s -> System.out.println(s));\n+ sayHi(System.out::println);\n }\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javac","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dexes","attrs":{}}],"attrs":{}},{"type":"text","text":" 與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 的編譯是相同的,與 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本有一個顯著的區別。在編譯爲 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"dalvik","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼時,生成的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 類的主體已更改。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"[000268] -$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM.log:(Ljava/lang/String;)V\n0000: iget-object v0, v1, L-$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM;.f$0:Ljava/io/PrintStream;\n0002: invoke-virtual {v0, v2}, Ljava/io/PrintStream;.println:(Ljava/lang/String;)V\n0005: return-void\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"不是通過生成 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java8.lambda$main$0","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法然後調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.out.println","attrs":{}}],"attrs":{}},{"type":"text","text":" 的方式實現,而是直接調用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.out.println","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式調用類也不是一個靜態單例,而是直接使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"PrintStream","attrs":{}}],"attrs":{}},{"type":"text","text":" 類實例引用,即 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.out","attrs":{}}],"attrs":{}},{"type":"text","text":",它的調用如下。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"[0002bc] Java8.main:([Ljava/lang/String;)V\n0000: sget-object v1, Ljava/lang/System;.out:Ljava/io/PrintStream;\n0003: new-instance v0, L-$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM;\n0004: invoke-direct {v0, v1}, L-$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM;.:(Ljava/io/PrintStream;)V\n0008: invoke-static {v0}, LJava8;.sayHi:(LJava8$Logger;)V\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣我們也可以在源碼級層面進行模擬。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":" public static void main(String... args) {\n- sayHi(System.out::println);\n+ sayHi(new -$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM(System.out));\n }\n@@\n }\n+\n+class -$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM implements Java8.Logger {\n+ private final PrintStream ps;\n+\n+ -$$Lambda$1Osqr2Z9OSwjseX_0FMQJcCG_uM(PrintStream ps) {\n+ this.ps = ps;\n+ }\n+\n+ @Override public void log(String s) {\n+ ps.println(s);\n+ }\n+}\n","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"7. Interface Methods(接口中的方法)","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 中新增了接口方法中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"default","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"static","attrs":{}}],"attrs":{}},{"type":"text","text":" 修飾符。接口中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"static","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法允許直接操作調用。接口中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"default","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法允許你爲接口添加默認實現方法。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"interface Logger {\n void log(String s);\n\n default void log(String tag, String s) {\n log(tag + \": \" + s);\n }\n\n static Logger systemOut() {\n return System.out::println;\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 中的脫糖都已經支持了這兩個接口的新特性。通過上面的方法同樣可以分析出脫糖是如何進行優化工作的,具體的分析就留給讀者了。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"8. Just Use Kotlin?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這個時候肯定有很多讀者猜想 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Kotlin","attrs":{}}],"attrs":{}},{"type":"text","text":" 是否也具備這種能力。當然,Kotlin 同樣提供了 lambda 和接口中的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"static","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"default","attrs":{}}],"attrs":{}},{"type":"text","text":" 方法。這些特性都被 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"kotlinc","attrs":{}}],"attrs":{}},{"type":"text","text":" 以相同的方式實現。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"VM","attrs":{}}],"attrs":{}},{"type":"text","text":" 的開發者肯定會 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"100%","attrs":{}}],"attrs":{}},{"type":"text","text":" 支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Kotlin","attrs":{}}],"attrs":{}},{"type":"text","text":" 實現 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言的新特性。因爲每次的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 新版本都會在字節碼構建和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"VM","attrs":{}}],"attrs":{}},{"type":"text","text":" 上帶來新的優化體驗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在未來和可能 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Kotlin","attrs":{}}],"attrs":{}},{"type":"text","text":" 不會支持 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 6","attrs":{}}],"attrs":{}},{"type":"text","text":" 和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 7","attrs":{}}],"attrs":{}},{"type":"text","text":",","attrs":{}},{"type":"link","attrs":{"href":"https://blog.jetbrains.com/idea/2015/12/intellij-idea-16-eap-144-2608-is-out/","title":"","type":null},"content":[{"type":"text","text":"Intellij 開發工具已經在在 2016 年 1 月遷移至 Java 8","attrs":{}}]},{"type":"text","text":"。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"9. Desugaring APIs","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"上面的分析中,我們一直關注的是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言新特性,其它還有一些主要的方面沒有提及,比如新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"APIs","attrs":{}}],"attrs":{}},{"type":"text","text":"。在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 轉給你帶來了很多新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"APIs","attrs":{}}],"attrs":{}},{"type":"text","text":",比如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"stream","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Optional","attrs":{}}],"attrs":{}},{"type":"text","text":"、","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"CompletableFuture","attrs":{}}],"attrs":{}},{"type":"text","text":" 以及新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"date/time API","attrs":{}}],"attrs":{}},{"type":"text","text":" 等等。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"回到上面的例子,我們使用新的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"date/time API","attrs":{}}],"attrs":{}},{"type":"text","text":" 來輸出日誌打印的時間。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.time.*;\n\nclass Java8 {\n interface Logger {\n void log(LocalDateTime time, String s);\n }\n\n public static void main(String... args) {\n sayHi((time, s) -> System.out.println(time + \" \" + s));\n }\n\n private static void sayHi(Logger logger) {\n logger.log(LocalDateTime.now(), \"Hello!\");\n }\n}\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們同樣使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javac","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"d8","attrs":{}}],"attrs":{}},{"type":"text","text":" 指令進行編譯:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ javac *.java\n\n$ java -jar d8.jar \\\n --lib $ANDROID_HOME/platforms/android-28/android.jar \\\n --release \\\n --output . \\\n *.class\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"當編譯完成後,我們可以將它運行在一個手機或模擬器中。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"bash"},"content":[{"type":"text","text":"$ adb push classes.dex /sdcard\nclasses.dex: 1 file pushed. 0.5 MB/s (1620 bytes in 0.003s)\n\n$ adb shell dalvikvm -cp /sdcard/classes.dex Java8\n2018-11-19T21:38:23.761 Hello\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"如果我們的設備運行在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API26","attrs":{}}],"attrs":{}},{"type":"text","text":" 或更高的版本上我們會得到一個帶有時間戳的日誌。但是在一個低於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API26","attrs":{}}],"attrs":{}},{"type":"text","text":" 的機器上,得到確實異常信息。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"java.lang.NoClassDefFoundError: Failed resolution of: Ljava/time/LocalDateTime;\n at Java8.sayHi(Java8.java:13)\n at Java8.main(Java8.java:9)\n","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"顯然,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 通過脫糖使 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"lambda","attrs":{}}],"attrs":{}},{"type":"text","text":" 表達式能夠運行在所有的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本機器上,但是卻沒有對新 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 做任何處理,所以我們無法使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"LocalDateTime","attrs":{}}],"attrs":{}},{"type":"text","text":" 類。也說明我們僅僅能夠利用部分的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 新特性,而不是全部。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"針對這種情況,開發者可自行編譯組件引用或使用相關的第三方實現庫來解決,但是退一步講,既然開發者可以自己編譯或實現,爲什麼 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 不能在脫糖中爲我們做這些呢?","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其實 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 現在僅僅針對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Throwable.addSuppressed","attrs":{}}],"attrs":{}},{"type":"text","text":" 這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 進行實現,這個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 是用於 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 7","attrs":{}}],"attrs":{}},{"type":"text","text":" 引入的語言特性 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"try-with-resources","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們需要 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8 API","attrs":{}}],"attrs":{}},{"type":"text","text":" 在所有設備上工作,我們所需要的只是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 團隊在他們的脫糖工具中添加支持來進行重寫。您可以在 ","attrs":{}},{"type":"link","attrs":{"href":"https://issuetracker.google.com/issues/114481425","title":"","type":null},"content":[{"type":"text","text":"Android 問題跟蹤程序","attrs":{}}]},{"type":"text","text":"上添加 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 功能請求,以傳達您的支持。","attrs":{}}]},{"type":"heading","attrs":{"align":null,"level":3},"content":[{"type":"text","text":"10. 總結","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"雖然一段時間以來,語言特性的脫糖已經以各種形式出現,但是缺乏對新 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 的適配仍然是我們生態系統中的一個巨大缺陷。不然直到絕大多數應用程序能夠指定最小 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API 26","attrs":{}}],"attrs":{}},{"type":"text","text":" 的那一天,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 工具鏈缺少 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 的缺陷纔算停止阻礙 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 庫生態系統的發展。儘管現在 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言特性脫糖是 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 的一部分,但默認情況下它沒有啓用。開發人員必須明確地選擇它們的源代碼和目標兼容性到 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 庫的作者可以通過使用 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 字節碼來構建和發佈它們的庫(即使你不使用語言特性)。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 正在積極工作,因此 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 語言和 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 支持的前景仍然光明。即使你僅僅是一個 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"KOTLIN","attrs":{}}],"attrs":{}},{"type":"text","text":" 用戶,重要的是要保持對 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Android","attrs":{}}],"attrs":{}},{"type":"text","text":" 的活力,以支持更好的字節碼和新 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"API","attrs":{}}],"attrs":{}},{"type":"text","text":" 的 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java","attrs":{}}],"attrs":{}},{"type":"text","text":" 新版本。在某些情況下,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"D8","attrs":{}}],"attrs":{}},{"type":"text","text":" 實際上是超越 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Java 8","attrs":{}}],"attrs":{}},{"type":"text","text":" 版本的,我們將在下一篇文章中進行探索。","attrs":{}}]}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章