Lambda expressions for generic target types
See original GitHub issueI am using InvokeDynamic.lambda(InDefinedShape, TypeDescription)
to create the equivalent of these two methods:
static interface StringMeasure
{
Integer measure(String string);
}
StringMeasure stringMeasure()
{
return String::length;
}
Function<String, Integer> stringLengthFunction()
{
return String::length;
}
This works as expected for the stringMeasure()
method, but I’m having a hard time getting Byte Buddy to generate correct code for the stringLengthFunction()
method. So far, my efforts have yielded NoSuchMethodError
s, AbstractMethodError
s, or LambdaConversionException: Invalid receiver type
.
I noticed that there is a slight difference in the bootstrap methods generated for the two cases.
For stringMeasure()
the Java compiler produces the following:
fava.tests.ByteBuddyTest$StringMeasure stringMeasure();
Code:
0: invokedynamic #16, 0 // InvokeDynamic #0:measure:()Lfava/tests/ByteBuddyTest$StringMeasure;
5: areturn
...
BootstrapMethods:
0: #38 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#40 (Ljava/lang/String;)Ljava/lang/Integer;
#47 REF_invokeVirtual java/lang/String.length:()I
#48 (Ljava/lang/String;)Ljava/lang/Integer;
For the other method, the Java compiler’s output looks a little different:
java.util.function.Function<java.lang.String, java.lang.Integer> stringLengthFunction();
Code:
0: invokedynamic #23, 0 // InvokeDynamic #1:apply:()Ljava/util/function/Function;
5: areturn
...
1: #38 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#50 (Ljava/lang/Object;)Ljava/lang/Object;
#51 REF_invokeVirtual java/lang/String.length:()I
#52 (Ljava/lang/String;)Ljava/lang/Integer;
The difference is the first argument to the bootstrap method: when targeting the (non-parameterized) StringMeasure
interface we see the input and output types as expected: (Ljava/lang/String;)Ljava/lang/Integer;
.
When targeting the java.util.function.Function
interface, the first argument corresponds to the type erasure of the interface, i.e., (Ljava/lang/Object;)Ljava/lang/Object;
, whereas the third argument appears to be the fully reified signature.
Both of these examples were collected from the output of the Java compiler. I can generate the code for the first example in Byte Buddy, but the second example is giving me trouble: I end up with (Ljava/lang/Object;)Ljava/lang/Object;
for both the first and the third bootstrap method argument.
This even makes somewhat sense: the method description is passed as a MethodDescription.InDefinedShape
, which does not resolve type variables, and the functional interface type is passed as a TypeDescription
, which again can only represent non-generic types.
It seems to me that one would need the capability to pass the functional interface as a Generic
type, to address this case. Is this a current limitation of Byte Buddy, or is there a different way to generate lambda expressions for generic interfaces?
Issue Analytics
- State:
- Created 2 years ago
- Comments:8 (4 by maintainers)
This week, I think.
Will there be another Byte Buddy release this month? I’m just curious when I can expect this to be available in Maven Central. Thanks!