JDK 8 - Lambda - Reflection issues
Remi Forax
forax at univ-mlv.fr
Fri Mar 14 18:14:14 UTC 2014
On 03/13/2014 09:56 AM, Pavel Bucek wrote:
> Hi Remi,
>
> thanks! I suspected that it would not be possible, but it is always
> better to have the confirmation.
>
> My context is slightly different, but the consequences are similar
> like with the jackson library. Seems a little unfortunate to have the
> possibility to use lambdas even on places where it cannot work - my
> guess is that this will become one of most frequent question on some
> mailing lists..
>
> (you don't really need to read further)
>
> I'm working on JSR 356 - Java API for WebSocket implementation and
> there is an interface:
>
> interface MessageHandler.Whole<T> extends MessageHandler {
> void onMessage(T message);
> }
>
> And then there is a Session object, which has method "void
> addMessageHandler(MessageHandler handler);". Obvious common use of
> this method is:
>
> session.addMessageHandler(new MessageHandler.Whole<String>() {
> @Override
> public void onMessage(String message) {
> // ...
> }
> });
>
> I can see my IDE automatically offers me to transform this to lambda
> expression (this is actually what worries me a little, because all
> users will see that and do it - because why not - it seems to be
> equivalent with anonymous class). When this suggestion is accepted,
> previous statement is transformed into:
>
> session.addMessageHandler((MessageHandler.Whole<String>) message -> {
> // ...
> });
>
> which looks prettier, but just does not work and cannot work :/ I
> guess we could provide wrapper class, something like:
>
> public static class WholeMessageHandler<T> implements
> MessageHandler.Whole<T> {
>
> private final Class<T> genericParam;
> private final MessageHandler.Whole<T> wholeMessageHandler;
>
> protected WholeMessageHandler(Class<T> genericParam,
> MessageHandler.Whole<T> wholeMessageHandler) {
> this.genericParam = genericParam;
> this.wholeMessageHandler = wholeMessageHandler;
> }
>
> public Class<T> getGenericParam() {
> return genericParam;
> }
>
> @Override
> public void onMessage(T message) {
> wholeMessageHandler.onMessage(message);
> }
> }
>
> and then, when user would want to use lambdas, do it with help of that
> class:
>
> session.addMessageHandler(new WholeMessageHandler<Main>(Main.class,
> param -> System.out.println("234")));
>
> so I can do this like a workaround, but anyway, the initial
> recommendation of the IDE is bad enough to cause us lots of mailing
> list traffic and explanations why is not possible to use lambda in
> "native" fashion. Anyway - I'm not describing my pain just to share my
> pain - if anyone have any suggestions how this can be solved, I'm all
> ears; any reply would be greatly appreciated.
>
> Thanks!
> Pavel
I should have written a blog post on that subject a long time ago,
doing runtime inspection of type information used by javac in a library
is usually harmful,
here is why.
1) Java is not the only language on the JVM, the other compiler/runtime
doesn't necessarily have type information,
by requiring type information you consider groovy, nashorn, jruby,
etc guys as second citizens.
2) The code that does the introspection is hard to get right,
a simple example that shows that you have to do type propagation:
interface Foo<I> extends MessageHandler.Whole<I> { }
class Bar extends Comparable<Integer>, Foo<String> { ... }
session.addMessageHandler(new Bar());
3) At runtime, it can be very slow because it may have to crawle all the
super types,
and look for a fix point when you have types that have recursive
bounds (like enums or comparable)
4) Let suppose the introspection code found a type, then it usually
relies on an implicit configuration
to find how to convert the value (usually a String) to the type.
Implicit configuration == nigthmare when testing if the
configuration used by the user code
is not the default one
So instead of using a broken idiom, I think it's better to explicitly
specify the conversion
using either a lambda or better a method reference as first parameter.
session.addMessageHandler(Integer:parseInt, integer -> ...);
session.addMessageHandler(Paths::get, path -> ...);
cheers,
Rémi
>
>
> On 12/03/14 19:42, Remi Forax wrote:
>> On 03/12/2014 07:12 PM, Pavel Bucek wrote:
>>> Hello all,
>>>
>>> I have an issue with getting generic parameter when using lambdas. I
>>> can get the type when using anonymous classes.
>>>
>>> code sample will be more descriptive than anything I would say, so.. :
>>>
>>> public class Main {
>>>
>>> public static interface A<T> {
>>> public void method(T param);
>>> }
>>>
>>> public static void main(String[] args) {
>>>
>>> final A<Main> anonClass = new A<Main>() {
>>> @Override
>>> public void method(Main param) {
>>> System.out.println("234");
>>> }
>>> };
>>>
>>> final A<Main> lambda = param -> System.out.println("234");
>>>
>>> //following does not help.
>>> // final A<Main> lambda = (A<Main>)param ->
>>> System.out.println("234");
>>>
>>>
>>> // output: Main.Main$A<Main>
>>> System.out.println("$ " +
>>> anonClass.getClass().getGenericInterfaces()[0]);
>>>
>>> // output: interface Main$A ### generic type info
>>> is already lost (no <Main>)
>>> System.out.println("# " +
>>> lambda.getClass().getGenericInterfaces()[0]);
>>>
>>> // parameterized type from annon class
>>> final Type t =
>>> ((ParameterizedType)anonClass.getClass().getGenericInterfaces()[0]).getActualTypeArguments()[0];
>>> System.out.println("$ " + t);
>>>
>>> // parameterized type from lambda
>>> System.out.println("# " + "???");
>>> }
>>> }
>>>
>>> I was not able to find any useful documentation or article about
>>> this, so sorry if this is something common - feel free to RTM me
>>> (with relevant link please).
>>>
>>> Thanks and regards,
>>> Pavel
>>
>> As you have seen a lambda is not an anonymous class :)
>>
>> A non-serializable lambda is more lightweight than an anonymous class
>> so the generic information that are transmitted from the bytecode to
>> the runtime (the lambda metafactory) are not kept inside the lambda
>> class (a lambda class may be shared by several different lambdas).
>>
>> A serializable lambda keep these information because you need them to
>> deserialize a lambda but they are not publicly available
>> The current implementation encoded them in the bytecode and this
>> bytecode is not publicly available so unless you serialize the lambda
>> and serialize it by hand, you can not have access to these information.
>>
>> So you can not use a lambda with frameworks like Jackson that use the
>> TypeReference idiom,
>> you can still use an anonymous class for that :)
>>
>> cheers,
>> Rémi
>>
>>
>>
>
More information about the jdk8-dev
mailing list