Reputation: 9383
Java 8 introduces support for first-class functions, which allows assigning functions to variables. In this case the variables must be of a function type, which is defined by a functional interface (an interface with just one abstract method).
So, considering an example of an interface I
and a class A
with the following definition:
interface I{ int foo(); }
class A implements I{
public int foo(){return 7;}
public static int bar(){return 11;}
}
We can assign to a variable of type I
an instance of A
or a method reference to the method bar
of A
. Both can be store on variables of type I
, such as:
I i1 = new A();
I i2 = A::bar;
If we analyze the bytecodes resulting from the compilation of the previous code we will get:
0: new #2 // class A
3: dup
4: invokespecial #3 // Method A."<init>":()V
7: astore_1
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
13: astore_2
For i1 = new A();
that is clearly that the corresponding instruction 7: astore_1
is storing an instance of A
that is compatible with I
. But, as a result of the i2 = A::bar
we are storing the result of the 8: invokedynamic #4, 0
.
So, that means that the result of an invokedynamic
is always an instance of the target type, which is the type of the variable that we are assigning with a method reference?
Upvotes: 2
Views: 198
Reputation: 298409
The result of the invokedynamic
instruction, the way Java 8’s lambda expressions and method references use it, is indeed an instance of the target functional interface
.
It isn’t the result of the invokedynamic
instruction that is remembered by the JVM but the CallSite
that is returned by the bootstrap method, in case of the new Java 8 features one of the two methods of the LambdaMetafactory
.
The CallSite
instances linked to an invokedynamic
instruction encapsulate behavior, not a particular result value. The actual behavior provided by the LambdaMetafactory
is intentionally unspecified to provide a wide degree of freedom but the current implementation exhibits two different behaviors.
For non-capturing lambda expressions, the behavior will be to return a single instance which has been created during the invokedynamic
bootstrapping. This can be done by creating a constant wrapping MethodHandle
wrapped in a ConstantCallSite
. In this case, subsequent executions of the invokedynamic
instruction will evaluate to this instance.
For lambda expressions which capture values, the instruction will be linked against a constructor or factory method of a generated class which accepts the captured values. So then, subsequent executions of the invokedynamic
instruction will behave like an ordinary object construction (which creates a new instance of the class which implements the target interface
each time).
Upvotes: 4
Reputation: 98505
Each invokedynamic
bytecode refers to a corresponding CONSTANT_InvokeDynamic_info structure in the constant pool. This structure contains a Method Descriptor that is used to derive the types of the arguments and the type of return value for this invokedynamic
instruction.
In your example the method descriptor is ()LI;
computed during source-to-bytecode translation.
8: invokedynamic #4, 0 // InvokeDynamic #0:foo:()LI;
^^^^^
It means that this particular bytecode expects no arguments and always produces the result of type I
.
Upvotes: 6