An in-depth understanding of lambda expressions in Java _java

Source: Internet
Author: User
Tags mixed static class

Java 8 begins to emerge with a new feature: Functional Programming using LAMBDA expressions (JSR-335). What we're going to talk about today is a subset of the LAMBDA: the virtual extension method, also known as the Public Defender (defender) approach. This feature allows you to provide a default implementation of the method in the interface definition. For example, you can declare a method definition for an existing interface, such as a List and a Map, so that other developers do not have to implement these methods again, a bit like an abstract class, but actually an interface. Of course, Java 8 is theoretically compatible with existing libraries.

The virtual extension method brings multiple inheritance features to Java, although the team claims that, unlike multiple inheritance, virtual extension methods are limited to behavior inheritance. Perhaps through this feature you can see the shadow of multiple inheritance. But you can still simulate the inheritance of the instance state. I will describe in detail in the following article the inheritance of the implementation state in Java 8, which is mixed with mixin.

What is mixed with mixin?

Mixing is a combination of abstract classes, mainly used in multiple inheritance contexts to add multiple services for a class, and multiple inheritance to synthesize multiple mixin groups into your class. For example, if you have a class that represents a "horse," you can instantiate the class to create an instance of "horse" and then extend it by inheriting like a "garage" and "garden", using Scala's writing:

Val myhouse = new house with garage with Garden

Inheritance from Mixin is not a specific specification, it's just a way to add a variety of functionality to an existing class. In OOP, with Mixin, you have the readability of the class through it.

For example, there is a way to use Mixin in Python's Socketserver module, where mixin helps 4 services based on different sockets, including UDP and TCP services that support multiple processes, and UDP and TCP services that support multithreading.

Class Forkingudpserver (Forkingmixin, udpserver): Pass
class Forkingtcpserver (Forkingmixin, TCPServer):
 
Pass Class Threadingudpserver (ThreadingMixIn, udpserver): Pass
class Threadingtcpserver (ThreadingMixIn, TCPServer): Pass

What is a virtual extension method?


Java 8 introduces the concept of a virtual extension method, also known as Public defender methods. Let's simplify this concept to vem.

Vem is designed to provide a default method definition for the Java interface, and you can use it to add new method definitions to existing interfaces, such as the collection APIs in Java. Such third-party libraries like Hibernate do not need to repeat all the methods of implementing these collection APIs because some default methods are already available.

The following is an example of how to define a method in an interface:

Public interface Collection<t> extends iterable<t> {
 
  <R> collection<r> filter (predicate <T> p)
    default {return Collections.<t>filter (this, p);}
 
}

Simulation of mixed Java 8 pairs

Now we're going to do a mixed effect with vem, but the warning is: please don't use it at work!

The following implementation is not thread-safe, and there may be a memory leak problem, depending on the hashcode and equals method defined in your class, which is another drawback, which I will discuss later.

First we define an interface (impersonate the state bean) and provide the default definition of the method:

Public interface Switchablemixin {
  boolean isactivated () default {return switchables.isactivated (this);}
  void Setactivated (Boolean activated) default {switchables.setactivated (this, activated);}

Then we define a tool class that contains a Map instance to hold the association of the instance and the state, which is represented by a private nested class in the tool class:

Public final class Switchables {
 
  private static final map<switchablemixin, switchabledevicestate> Switch_ STATES = new hashmap<> ();
 
  public static Boolean isactivated (Switchablemixin device) {
    switchabledevicestate state = Switch_states.get (device );
    return state!= null && state.activated;
  }
 
  public static void setactivated (Switchablemixin device, Boolean activated) {
    switchabledevicestate state = Switch_ States.get (device);
    if (state = = null) {state
      = new Switchabledevicestate ();
      Switch_states.put (device, state);
    state.activated = activated;
  }
 
  private static class Switchabledevicestate {
    private boolean activated;
  }
 
}

Here is a use case that highlights the inheritance of the state:

private static Class Device {}
 
private static class Devicea extends Device implements Switchablemixin {}
 
private S Tatic class Deviceb extends Device implements Switchablemixin {}

"Something totally different."

The implementation above seems quite normal, but Oracle's Java language architect Brian Goetz me with a question that the current implementation is not working (assuming thread safety and memory leak issues have been resolved)

Interface Fakebrokenmixin {
  static map<fakebrokenmixin, string> backingmap
    = Collections.synchronizedmap (New Weakhashmap<fakebrokenmixin, string> ());
 
  String getName () default {return Backingmap.get (this);}
  void SetName (String name) default {backingmap.put (this, name);}
 
Interface x extends Runnable, fakebrokenmixin {}
 
x Makex () {return ()-> {System.out.println ("X");};}
 
  X x1 = Makex ();
  X x2 = Makex ();
  X1.setname ("x1");
  X2.setname ("X2");
 
  System.out.println (X1.getname ());
  System.out.println (X2.getname ());


What do you think will come out after this code is executed?
the resolution of the question

At first glance, the code for this implementation is fine. X is an interface that contains only one method, because GetName and SetName already have the default definition, but the run method of the Runable interface is undefined, so we can generate an instance of X through a lambda expression and then provide an implementation of the Run method, like make X that way. Therefore, you want this program to be executed after the results are displayed:

X1
x2

If you delete the call to the GetName method, the execution result becomes:

mytest$1@30ae8764
mytest$1@123acf34

These two lines show that the execution of the Makex method comes from two different instances, while the current OpenJDK 8 is generated (here I am using OpenJDK 8 24.0-b07).

Regardless, the current OpenJDK 8 does not reflect the final Java 8 behavior, in order to solve this problem, you need to use special parameter-xdlambdatomethod to run the Javac command, after using this parameter, the running result becomes:


X2
x2

If the GetName method is not invoked, it is displayed:

mytest$ $Lambda $1@5506d4ea
mytest$ $Lambda $1@5506d4ea

Each call to the Makex method seems to be a singleton instance from the same anonymous inner class, and if you look at the directory containing the compiled Java class file, you will find that there is no file named mytestclass$ $Lambda $1.class.

Because the lambda expression does not have a complete translation at compile time, the translation process is actually done at compile and run time, and the Javac compiler converts the lambda expression into the new instruction Invokedynamic (JSR292) of the JVM. This instruction contains all the necessary meta information about executing a lambda expression at run time. Includes the method name to invoke, the input output type, and a method named Bootstrap. The bootstrap method is used to define an instance that receives this method call, and once the JVM executes the invokedynamic instruction, the JVM will raise the Lambda Meta Factory Method (lambda metafactory methods) on a specific bootstrap.

To go back to that question, the lambda expression turned into a private static method ()-> {System.out.println ("X");} was transferred to MyTest:

private static void Lambda$0 () {
  System.out.println ("X");
}

If you use the JAVAP counter compiler and you can see this method using the-private parameter, you can also use the-c parameter to see a more complete conversion.

When you run the program, the JVM invokes the lambda Metafactory method to attempt to interpret the invokedynamic instruction. In our example, the first time that Makex is invoked, the lambda Metafactory method generates an instance of X and dynamically links the Run method to lambda$0 methods. An instance of X is then stored in memory, which is read directly from memory when the second call to Makex, so that the second instance of your call is the same as the first time.
Did you fix it? Is there a way out?

There is no immediate fix or solution to this problem. Although Oracle's Java 8 program activates the-xdlambdatomethod parameter by default, because this parameter is not part of the JVM specification, the implementations of different vendors and JVMs are different. For a lambda expression, the only thing you can expect is to implement your interface method in the class.


The other way

So far, although our imitation of mixin is not compatible with Java 8, it is possible to add multiple services to an existing class through multiple inheritance and delegation. This method is the virtual field pattern (dummy mode).

So take a look at our switchable.

Interface Switchable {  boolean isactive ();
  void SetActive (Boolean active);
}

We need a switchable interface and provide an additional abstract method to return the switchable implementation. The integrated approach contains the default definitions, which use the getter to convert to the invocation of the switchable implementation:


Public interface Switchableview extends switchable {
  switchable getswitchable ();
 
 
  Boolean isactive () default {return getswitchable (). IsActive ();}
  void SetActive (Boolean active) default {getswitchable (). setactive (active);}
}

Next, we create a complete switchable implementation:

public class Switchableimpl implements switchable {
 
 
  private boolean active;
 
 
  @Override Public
  Boolean isactive () {return
    active;
  }
 
 
  @Override public
  void SetActive (Boolean active) {
    this.active = active;
  }
}

Here is an example of how we use the virtual field pattern:

public class Device {} public
 
 
class Devicea extends Device implements Switchableview {
  Private switchable switch able = new Switchableimpl ();
 
 
  @Override public
  switchable getswitchable () {return
    switchable;
  }
}
 
 
public class Deviceb extends Device implements Switchableview {
  private switchable switchable = new Switchableimpl ();
 
 
  @Override public
  switchable getswitchable () {return
    switchable;
  }
}

Conclusion

In this article, we use two methods to add multiple services to a class through the Java 8 virtual extension method. The first method uses a MAP to store the instance state, which is dangerous because it is not thread safe and there is a memory leak problem, which relies entirely on the implementation of the Java language by different JVMs. Another approach is to use the virtual field pattern to return the final implementation instance through an abstract getter. The second approach is more independent and more secure.

Virtual extension method is a new feature of Java, this article is mainly about the implementation of multiple inheritance, you will have more in-depth research and application of other aspects, don't forget to share with you.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

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.