Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

How to write Lambda expressions with Scala and Java8

2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Internet Technology >

Share

Shulou(Shulou.com)06/01 Report--

This article focuses on "how to write Lambda expressions with Scala and Java8". Interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn how to write Lambda expressions by Scala and Java8.

The way Java is written-

1List names = Arrays.asList ("1", "2", "3")

2Stream lengths = names.stream () .map (name-> name.length ())

The way Scala is written-

1.val names = List ("1", "2", "3")

2.val lengths = names.map (name = > name.length)

It looks very simple on the surface, so what's wrong with the complexity behind it?

Analyze the implementation of Scala together.

The Code

I use javap (a tool that comes with jdk) to see the bytecode content contained in the class class compiled by the Scala compiler. Let's take a look at the final bytecode (this is what JVM will actually execute)

1.Compact / load the names object reference and push it into the operation stack (JVM treats it as a variable # 2)

2.Compact / it will stay for a while until called by the map function.

3.aload_2

The next thing becomes more interesting, and an instance of a composite class generated by the compiler is created and initialized. From a JVM point of view, it is through this object that the Lambda method is held. Interestingly, although Lambda is defined as a part of our method, it actually exists completely outside of our class.

New myLambdas/Lambda1 $$anonfun$1 / / new a lambda instance variable.

Dup / / pushes the lambda instance variable reference into the operation stack.

/ / finally, call its constructor. Remember, for JVM, it's just a normal object.

Invokespecial myLambdas/Lambda1 $$anonfun$1/ () V

/ / the code for these two governors loads the immutable.List CanBuildFrom factory used to create the list.

/ / this factory pattern is part of the Scala collection architecture.

Getstatic scala/collection/immutable/List$/MODULE$

Lscala/collection/immutable/List$

Invokevirtual scala/collection/immutable/List$/canBuildFrom ()

Lscala/collection/generic/CanBuildFrom

/ / now we have Lambda objects and factories in our operation stack

/ / the next step is to call the map function.

/ / if you remember, we initially pushed the names object reference to the top of the operation stack.

/ / the names object is now used as an instance of the map method call

/ / it can also accept Lambda objects and factories to generate a new collection that contains the length of the string.

Invokevirtual scala/collection/immutable/List/map (Lscala/Function1

Lscala/collection/generic/CanBuildFrom;) Ljava/lang/Object

But wait, what's going on inside the Lambda object?

Lambda object

The Lambda class is derived from scala.runtime.AbstractFunction1. The overwritten apply method can be called polymorphically by calling the map function. The code of the rewritten apply method is as follows:

Aload_0 / / load the this object reference to the operation stack

Aload_1 / / load string parameters to the operation stack

Checkcast java/lang/String / / check whether it is a string type

/ / call the apply method overridden in the composite class

Invokevirtual myLambdas/Lambda1 $$anonfun$1/apply (Ljava/lang/String;) I

/ / return value of packaging

Invokestatic scala/runtime/BoxesRunTime/boxToInteger (I) Ljava/lang/Integer

Areturn

The code that is actually used to perform the length () operation is nested in an additional apply method to simply return the length of the string we expect.

After a long walk ahead of us, we finally reached this side:

Aload_1

Invokevirtual java/lang/String/length () I

Ireturn

For the simple code we wrote above, we ended up generating a lot of bytecode, an extra class, and a bunch of new methods. Of course, this does not mean that we will give up using Lambda (we are writing scala, not C). This only shows the complexity behind these structures. Imagine that the code and complexity of Lambda expressions will be compiled into a complex chain of execution.

I expect Java8 to implement Lambda in the same way, but surprisingly, they use a completely different approach.

Java 8-A new way to implement

In the implementation of Java8, the bytecode is relatively short, but what you do is unexpected. It simply loads the names variable and calls its stream method, but what it does next is elegant. It uses a new instruction invokeDynamic added by Java7 to dynamically connect the real call point of the lambda function instead of creating an object that wraps the lambda function.

Aload_1 / / loads the names object reference and pushes it into the operation stack

/ / call its stream () method

Invokeinterface java/util/List.stream: () Ljava/util/stream/Stream

/ / Magic invokeDynamic instruction!

Invokedynamic # 0:apply: () Ljava/util/function/Function

/ / call the map method

Invokeinterface java/util/stream/Stream.map:

(Ljava/util/function/Function;) Ljava/util/stream/Stream

Magical InvokeDynamic instruction. This is a new directive in JAVA 7, which makes JVM less restrictive and allows dynamic language runtime binding symbols.

Dynamic link. If you look at the invokedynamic directive, you will find that there is actually no reference to the Lambda function (named lambda$0) because of the way invokedynamic is designed, simply the name and signature of the lambda, as in our example-

/ / A method called Lamda$0, which takes a string parameter and returns an Integer object

Lambdas/Lambda1.lambda$0: (Ljava/lang/String;) Ljava/lang/Integer

They are saved in an entry in a separate table in the .class file, and the # 0 parameter is passed to the instruction pointer when invokedynamic is executed. This new table does change the structure of the bytecode specification for the first time many years later, which requires us to adapt Takipi's error analysis engine to match it.

The Lambda code

The following bytecode is a real lambda expression. Then simply load the string parameters, call the length method to get the length, and wrap the return value. Note that it is compiled as a static method, thus avoiding passing an additional this object to him, as we saw earlier in Scala.

Aload_0

Invokevirtual java/lang/String.length: ()

Invokestatic java/lang/Integer.valueOf: (I) Ljava/lang/Integer

Areturn

Another advantage of the invokedynamic approach is that it allows us to call this method polymorphically using map functions without instantiating an encapsulated object or calling an overridden method. Very cool!

At this point, I believe you have a deeper understanding of "how to write Lambda expressions with Scala and Java8". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Internet Technology

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report