Java 8中Lambda表达式的阴暗面
這篇文章可能不會使我成為任何新朋友。 哦,好吧,無論如何我從來沒有真正在學校受到歡迎。 但是,讓我們說清楚。 就語言而言,Java 8的最大特色無疑是Lambda表達式。 幾年來,它一直是功能語言(例如Scala和Clojure)的旗艦功能,現在Java終于加入了。
第二大功能(當然取決于您問誰)是Nashorn –新的JVM JavaScript引擎,可以使Java與其他JS引擎(例如V8和它的node.js容器)相提并論。
但是這些新功能對他們不利。
我會解釋。 Java平臺由兩個主要組件構建而成。 JIT編譯并執行字節碼的JRE,JDK包含開發工具和javac源代碼編譯器。 這兩個組件是完全(但不是完全)分離的,這使人們能夠編寫自己的JVM語言,而Scala在最近幾年中日益受到關注。 其中存在一些問題。
JVM被構建為與語言無關,只要它可以轉換為字節碼,就可以執行以任何語言編寫的代碼。 字節碼規范本身是完全面向對象的,旨在與Java語言緊密匹配。 這意味著從Java源代碼編譯的字節碼在結構上將非常類似于它。
但是與Java的距離越遠,距離就越遠。 當您查看Scala是一種功能語言時,源代碼與執行的字節碼之間的距離就很大。 編譯器添加了大量合成類,方法和變量,以使JVM執行該語言所需的語義和流控制。
當您查看完全動態的語言(例如JavaScript)時 ,這種距離變得很大。
而現在有了Java 8,它也開始滲透到Java中。
那我為什么要在乎呢?
我希望這是一個理論性的討論,盡管有趣,但對我們的日常工作沒有實際意義。 不幸的是,它確實以很大的方式做到了。 隨著向Java中添加新元素的推動,代碼與運行時之間的距離越來越大,這意味著您正在編寫的內容和正在調試的內容將是兩件事。
要了解讓我們如何(重新)訪問下面的示例。
Java 6和7
這是傳統的方法,通過該方法我們可以遍歷一串字符串以映射其長度。
// simple check against empty strings public static int check(String s) {if (s.equals("")) {throw new IllegalArgumentException();}return s.length(); }//map names to lengthsList lengths = new ArrayList();for (String name : Arrays.asList(args)) {lengths.add(check(name)); }如果傳遞一個空字符串,這將引發異常。 堆棧跟蹤看起來像–
at LmbdaMain.check(LmbdaMain.java:19) at LmbdaMain.main(LmbdaMain.java:34)在這里,我們看到了所看到的堆棧跟蹤與所編寫的代碼之間的1:1關系,這使得調試此調用堆棧非常簡單。 這就是大多數Java開發人員所習慣的。
現在讓我們看一下Scala和Java 8。
斯卡拉
讓我們看看Scala中的相同代碼。 在這里,我們有兩個重大變化。 第一個是使用Lambda表達式映射長度,第二個是迭代是由框架執行的(即內部迭代)。
val lengths = names.map(name => check(name.length))在這里,我們真的開始注意到您編寫的代碼外觀與JVM(以及您)在運行時如何看到它們之間的區別。 如果拋出異常,則調用堆棧會長一個數量級 ,并且很難理解。
at Main$.check(Main.scala:6) at Main$$anonfun$1.apply(Main.scala:12) at Main$$anonfun$1.apply(Main.scala:12) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.TraversableLike$class.map(TraversableLike.scala:244) at scala.collection.AbstractTraversable.map(Traversable.scala:105) at Main$delayedInit$body.apply(Main.scala:12) at scala.Function0$class.apply$mcV$sp(Function0.scala:40) at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.App$$anonfun$main$1.apply(App.scala:71) at scala.collection.immutable.List.foreach(List.scala:318) at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32) at scala.App$class.main(App.scala:71) at Main$.main(Main.scala:1) at Main.main(Main.scala)*請記住,此示例非常簡單。 使用現實世界中的嵌套Lambda和復雜結構,您將需要更長的綜合調用堆棧,您需要從中了解發生了什么。
長期以來,Scala一直是一個問題,這也是我們構建Scala Stackifier的原因之一。
現在在Java 8中
直到現在,Java開發人員都對此免疫。 隨著Lambda表達式成為Java不可或缺的一部分,這種情況將會改變。 讓我們看一下相應的Java 8代碼以及生成的調用堆棧。
Stream lengths = names.stream().map(name -> check(name));at LmbdaMain.check(LmbdaMain.java:19) at LmbdaMain.lambda$0(LmbdaMain.java:37) at LmbdaMain$$Lambda$1/821270929.apply(Unknown Source) at java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:193) at java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948) at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:512) at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:502) at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708) at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234) at java.util.stream.LongPipeline.reduce(LongPipeline.java:438) at java.util.stream.LongPipeline.sum(LongPipeline.java:396) at java.util.stream.ReferencePipeline.count(ReferencePipeline.java:526) at LmbdaMain.main(LmbdaMain.java:39)這與Scala變得非常相似。 我們為更短,更簡潔的代碼,更復雜的調試和更長的綜合調用堆棧付出了代價。
原因是,盡管javac已擴展為支持Lambda函數,但JVM仍然對它們不了解 。 這是Java人士的一項設計決策,目的是保持JVM在較低級別運行,而又不會在其規范中引入新的元素。
盡管您可以辯論此決定的優缺點,但這意味著作為Java開發人員,無論我們是否愿意,當我們拿到罰單時弄清楚這些調用堆棧的成本現在都落在我們肩上。
Java 8中JavaScript
Java 8引入了全新JavaScript編譯器。 現在,我們終于可以以高效,直接的方式集成Java + JS。 但是,我們編寫的代碼與我們調試的代碼之間的不協調之處比這里更大。
這與Nashorn中的功能相同–
ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine engine = manager.getEngineByName("nashorn");String js = "var map = Array.prototype.map \n"; js += "var a = map.call(names, function(name) { return Java.type(\"LmbdaMain\").check(name) }) \n"; js += "print(a)"; engine.eval(js);在這種情況下,字節代碼是在運行時使用Lambda表達式的嵌套樹動態生成的 。 我們的源代碼與由JVM執行的結果字節碼之間幾乎沒有關聯。 現在,調用堆棧要長兩個數量級 。 用T先生的凄美的話語- 我可憐那些需要調試調用堆棧的傻瓜 ,您將到達這里。
有任何疑問嗎? (假設您可以一直滾動到此調用堆棧下方)。 在評論部分讓我知道。
LmbdaMain [Java Application] LmbdaMain at localhost:51287 Thread [main] (Suspended (breakpoint at line 16 in LmbdaMain)) LmbdaMain.wrap(String) line: 16 1525037790.invokeStatic_L_I(Object, Object) line: not available 1150538133.invokeSpecial_LL_I(Object, Object, Object) line: not available 538592647.invoke_LL_I(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 2150540.interpret_I(MethodHandle, Object, Object) line: not available 538592647.invoke_LL_I(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 92150540.interpret_I(MethodHandle, Object, Object) line: not available 38592647.invoke_LL_I(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 731260860.interpret_L(MethodHandle, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LL_L(MethodHandle, Object[]) line: 1108 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available 1597655940.invokeSpecial_LLLL_L(Object, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available 1353530305.linkToCallSite(Object, Object, Object, Object) line: not available Script$\^eval\_._L3(ScriptFunction, Object, Object) line: 3 1596000437.invokeStatic_LLL_L(Object, Object, Object, Object) line: not available 1597655940.invokeSpecial_LLLL_L(Object, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 282496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available 93508253.invokeSpecial_LLLLJL_L(Object, Object, Object, Object, Object, long, Object) line: not available 1850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 282496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available 293508253.invokeSpecial_LLLLJL_L(Object, Object, Object, Object, Object, long, Object) line: not available 1850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 1840903588.interpret_L(MethodHandle, Object, Object, Object, Object, long, Object) line: not available 2063763486.reinvoke(Object, Object, Object, Object, Object, long, Object) line: not available 850777594.invoke_LLLLJL_L(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 82496973.interpret_L(MethodHandle, Object, Object, Object, long, Object) line: not available 220309324.invokeExact_MT(Object, Object, Object, Object, long, Object, Object) line: not available NativeArray$10.forEach(Object, long) line: 1304 NativeArray$10(IteratorAction).apply() line: 124 NativeArray.map(Object, Object, Object) line: 1315 1596000437.invokeStatic_LLL_L(Object, Object, Object, Object) line: not available 504858437.invokeExact_MT(Object, Object, Object, Object, Object) line: not available FinalScriptFunctionData(ScriptFunctionData).invoke(ScriptFunction, Object, Object...) line: 522 ScriptFunctionImpl(ScriptFunction).invoke(Object, Object...) line: 207 ScriptRuntime.apply(ScriptFunction, Object, Object...) line: 378 NativeFunction.call(Object, Object...) line: 161 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available 1740189450.invokeSpecial_LLL_L(Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLL_L(MethodHandle, Object[]) line: 1113 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 2619171.interpret_L(MethodHandle, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLL_L(MethodHandle, Object[]) line: 1113 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLL_L(MethodHandle, Object[]) line: 1118 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available 263793464.invokeSpecial_LLLLL_L(Object, Object, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 1484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available 587003819.invokeSpecial_LLLLLL_L(Object, Object, Object, Object, Object, Object, Object) line: not available 811301908.invoke_LLLLLL_L(MethodHandle, Object[]) line: not available 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 484673893.interpret_L(MethodHandle, Object, Object, Object, Object, Object) line: not available LambdaForm$NamedFunction.invoke_LLLLL_L(MethodHandle, Object[]) line: 1123 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available LambdaForm$NamedFunction.invokeWithArguments(Object...) line: 1147 LambdaForm.interpretName(LambdaForm$Name, Object[]) line: 625 LambdaForm.interpretWithArguments(Object...) line: 604 323326911.interpret_L(MethodHandle, Object, Object, Object, Object) line: not available 2129144075.linkToCallSite(Object, Object, Object, Object, Object) line: not available Script$\^eval\_.runScript(ScriptFunction, Object) line: 3 1076496284.invokeStatic_LL_L(Object, Object, Object) line: not available 1709804316.invokeExact_MT(Object, Object, Object, Object) line: not available FinalScriptFunctionData(ScriptFunctionData).invoke(ScriptFunction, Object, Object...) line: 498 ScriptFunctionImpl(ScriptFunction).invoke(Object, Object...) line: 207 ScriptRuntime.apply(ScriptFunction, Object, Object...) line: 378 NashornScriptEngine.evalImpl(ScriptFunction, ScriptContext, ScriptObject) line: 544 NashornScriptEngine.evalImpl(ScriptFunction, ScriptContext) line: 526 NashornScriptEngine.evalImpl(Source, ScriptContext) line: 522 NashornScriptEngine.eval(String, ScriptContext) line: 193 NashornScriptEngine(AbstractScriptEngine).eval(String) line: 264 LmbdaMain.main(String[]) line: 44翻譯自: https://www.javacodegeeks.com/2014/03/the-dark-side-of-lambda-expressions-in-java-8.html
總結
以上是生活随笔為你收集整理的Java 8中Lambda表达式的阴暗面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c5驾照备案流程(c5驾照备案)
- 下一篇: 在没有IDE的情况下编译和运行Java