This is a question that I wondered about since the lambdas had been introduced in Java, and inspired by a related question, I thought that I might bring it up here, to see whether there are any ideas.
(Side notes: There is a similar question for C#, but I did not find one for Java. The questions for Java about "storing a lambda in a variable" always referred to cases where the type of the variable was fixed - this is exactly what I'm trying to circumvent)
Lambda expressions receive the type that they need, via target type inference. This is all handled by the compiler. For example, the functions
static void useF(Function<Integer, Boolean> f) { ... }
static void useP(Predicate<Integer> p) { ... }
can both be called with the same lambda expression:
useF(x -> true);
useP(x -> true);
The expression will once manifest itself as a class implementing the Function<Integer,Boolean>
interface, and once as a class implementing the Predicate<Integer>
interface.
But unfortunately, there is no way of storing the lambda expression with a type that is applicable to both functions, like in
GenericLambdaType
lambda = x -> true;
This "generic lambda type" would have to encode the type of the method that can be implemented by the given lambda expression. So in this case, it would be
(Ljava.lang.Integer)Ljava.lang.Boolean
lambda = x -> true;
(based on the standard type signatures, for illustration). (This is not completely unreasonable: The C++ lambda expressions are basically doing exactly that...)
So is there any way to prevent a lambda expression being resolved to one particular type?
Particularly, is there any trick or workaround that allows the useF
and useP
methods sketched above to be called with the same object, as in
useF(theObject);
useP(theObject);
This is unlikely, so I assume the answer will plainly be: "No", but: Could there be any way to write a generic, magic adaption method like
useF(convertToRequiredTargetType(theObject));
useP(convertToRequiredTargetType(theObject));
?
Note that this question is more out of curiosity. So I'm literally looking for any way to achieve this (except for custom precompilers or bytecode manipulation).
There seem to be no simple workarounds. A naive attempt to defer the type inference, by wrapping the expression into a generic helper method, as in
static <T> T provide()
{
return x -> true;
}
of course fails, stating that "The target type of this expression must be a functional interface" (the type can simply not be inferred here). But I also considered other options, like MethodHandles, brutal unchecked casts or nasty reflection hacks. Everything seems to be lost immediately after the compilation, where the lambda is hidden in an anonymous object of an anonymous class, whose only method is called via InvokeVirtual
...
CallSite
? Basically, that is what a lambda is – VariometerFunction<Integer, Boolean>
toPredicate<Integer>
? Is this so you can have one lambda stored as a variable of one functional interface type but you can use it as another functional interface type? – SideboarduseP(x -> f.apply(x));
, but this does not work generically. – Napiform