Link to the local class constructor method

There are a few similar questions about SO about a reference to the constructor method of a local class, but I would like to clarify a slightly different thing. Consider the following code snippet:

static Callable gen(int i) { class X { int x = i; public String toString() { return "" + x; } } return X::new; } ... System.out.println(gen(0).call()); System.out.println(gen(1).call()); 

Obviously this will be a printout

 0 1 

It turns out that class X has a form constructor ...$X(int) (you can find it through X.class.getDeclaredConstructors() ).

But what is interesting is that the returned lambdas (or method references) are not a simple constructor reference ...$X(int) , for example, Integer::new . They internally call this constructor ...$X(int) with a predefined argument ( 0 or 1 ).

So I'm not sure, but it looks like this method reference is not exactly described in JLS. And there is no other way than this case for local classes to create this kind of lambda (with predefined constructor arguments). Who can help clarify this?

To be precise:

  • Where is the described method description type in JLS?

  • Is this any other way to create a method reference for an arbitrary class constructor with predefined arguments?

+5
source share
2 answers

You focus too much on low-level irrelevant details. At the byte code level, there may be a constructor that takes an int parameter, but at the language level you did not specify an explicit constructor, therefore, there will be a default constructor without any arguments, as with any other class.

This should become clear when you write pre-Java 8 code:

 static Callable<Object> gen(int i) { class X { int x = i; public String toString() { return "" + x; } } X x=new X(); โ€ฆ 

You create an instance of X by its default constructor without accepting any arguments. Your local class captures the value of i , but how it is done at a low level, i.e. That the constructor X has a synthetic parameter int , and the expression new passes the value i it, is an implementation detail.

You can even add an explicit constructor as

  X() {} 

without changing anything.

Obviously, you can also write the new X() expression inside the lambda expression here, since the expressions do not change their semantics when placed inside the lambda expression:

  return () -> new X(); 

or use its short form, method link

  return X::new; 

This is nothing special, the behavior is clear even without reference to the specification, if you forget about the distracting details of the low level. X can write as many local variables as you like, the number of constructors does not change (at the language level).

+3
source

This behavior is defined in the JLS section ยง15.13.3 :

If the form ClassType :: [TypeArguments] is new, the body of the invoke method has the effect of expressing the creation of an instance of a class of the form new [TypeArguments] ClassType (A1, ..., An), where the arguments A1, ..., An are formal parameters of the invocation method and Where:

  • The attached instance for the new object, if any, is derived from the site of the reference method expression, as specified in ยง15.9.2.

  • A constructor for invocation is a constructor corresponding to the declaration of the compilation time of a method reference (ยง15.13.1).

Although this indicates the inclusion of instances , the captured variables and parameters are not mentioned in ยง15.13.3.

As for your second question, you need to manually capture and change the parameter:

 static Callable gen(int i) { final int i1 = someCondition() ? i : 42; class X { int x = i1; // <- public String toString() { return "" + x; } } return X::new; } 
+3
source

Source: https://habr.com/ru/post/1240872/


All Articles