Discussion:
JDK 8 - Lambda - Reflection issues
Pavel Bucek
2014-03-12 18:12:24 UTC
Permalink
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
Remi Forax
2014-03-12 18:42:06 UTC
Permalink
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas. I
can get the type when using anonymous classes.
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
Pavel Bucek
2014-03-13 08:56:26 UTC
Permalink
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
Post by Remi Forax
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas. I
can get the type when using anonymous classes.
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
Pavel Bucek
2014-03-13 09:10:46 UTC
Permalink
just for the sake of correctness - WholeMessageHandler<T> does not need
to have Class<T> genericParam field and constructor param - anonymous
class which just wraps the lambda expression is good enough.
Post by Pavel Bucek
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
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
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,
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
which looks prettier, but just does not work and cannot work :/ I
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
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
Post by Remi Forax
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas. I
can get the type when using anonymous classes.
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
Pavel Bucek
2014-03-14 16:43:09 UTC
Permalink
I'm considering filing this as a backward compatibility issue against
JDK 8 - my reasoning would be that I should be able to still get the
same information from the reflection API as I did in previous versions
of JDK and I don't have a way how to restrict lambda usage in the
already designed and published API.

Can anyone give me any reason why shouldn't I do that? (I suspect
something like "read [this][link] document, where we explicitly say that
this is to be expected thus the issue is invalid".

Thanks!
Pavel
Post by Pavel Bucek
just for the sake of correctness - WholeMessageHandler<T> does not
need to have Class<T> genericParam field and constructor param -
anonymous class which just wraps the lambda expression is good enough.
Post by Pavel Bucek
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
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
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,
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
which looks prettier, but just does not work and cannot work :/ I
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
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
Post by Remi Forax
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas.
I can get the type when using anonymous classes.
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
Remi Forax
2014-03-14 18:26:40 UTC
Permalink
Post by Pavel Bucek
I'm considering filing this as a backward compatibility issue against
JDK 8 - my reasoning would be that I should be able to still get the
same information from the reflection API as I did in previous versions
of JDK and I don't have a way how to restrict lambda usage in the
already designed and published API.
Can anyone give me any reason why shouldn't I do that? (I suspect
something like "read [this][link] document, where we explicitly say
that this is to be expected thus the issue is invalid".
Thanks!
Pavel
You should do submit a bug, so we will document for everyone why this
issue is invalid :)

cheers,
R?mi
Post by Pavel Bucek
Post by Pavel Bucek
just for the sake of correctness - WholeMessageHandler<T> does not
need to have Class<T> genericParam field and constructor param -
anonymous class which just wraps the lambda expression is good enough.
Post by Pavel Bucek
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
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
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,
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
which looks prettier, but just does not work and cannot work :/ I
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
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
Post by Remi Forax
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas.
I can get the type when using anonymous classes.
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
Remi Forax
2014-03-14 18:14:14 UTC
Permalink
Post by Pavel Bucek
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
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
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,
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
which looks prettier, but just does not work and cannot work :/ I
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
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
Post by Pavel Bucek
Post by Remi Forax
Post by Pavel Bucek
Hello all,
I have an issue with getting generic parameter when using lambdas. I
can get the type when using anonymous classes.
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
Simone Bordet
2014-03-14 22:07:53 UTC
Permalink
Hi,
Post by Remi Forax
Post by Pavel Bucek
I'm working on JSR 356 - Java API for WebSocket implementation and there
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
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
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
As another co-implementor of JSR 356 in Jetty, I wanted to express the
same concerns as Pavel.
Post by Remi Forax
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.
Very good point.
Post by Remi Forax
2) The code that does the introspection is hard to get right,
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 -> ...);
In JSR 356 the type T in Whole<T> is used to select a "deserializer"
that converts the bytes in the WebSocket message to an object of type
T.
"Deserializers" are configurable by the application.

If I understand correctly R?mi suggestion, he's saying to change the
signature to accept a "converter" from String/byte[] to something
(above expressed as a method reference) and then feed the result of
that to the lambda.
addMessageHandler(Function<String, B> f, MessageHandler<B> h)

It would be probably much simpler to get rid of "deserializers" in JSR
356, and stick with String and byte[], and possibly streams and
readers.
The application does the rest.

Not that it will fix the problem at hand, and just for sake of
discussion, has the idea of an annotation that enforces *not* to
convert to a lambda been explored by experts ?

@NONFunctionalInterface
interface Foo<T> { void foo(T t); }

interface Bar { void <T> accept(Foo<T> foo); }

class Main {
void main(String[] a) {
Bar bar = ...;
bar.accept((String s) -> { ... }); // Does not compile
}
}

Thanks !
--
Simone Bordet
http://bordet.blogspot.com
---
Finally, no matter how good the architecture and design are,
to deliver bug-free software with optimal performance and reliability,
the implementation technique must be flawless. Victoria Livschitz
Florian Weimer
2014-03-25 08:54:40 UTC
Permalink
Post by Pavel Bucek
And then there is a Session object, which has method "void
addMessageHandler(MessageHandler handler);". Obvious common use of this
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
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
// ...
});
which looks prettier, but just does not work and cannot work :/
Why doesn't it work? Does the implementation try to extract the String
actual type argument? Then it's unsound because

session.addMessageHandler(new MessageHandler.Whole<T>() {
@Override
public void onMessage(T message) {
// ...
}
});

type-checks (assuming that T is a type parameter), but will not work at
run time, either.
--
Florian Weimer / Red Hat Product Security Team
Paul Sandoz
2014-03-13 10:09:52 UTC
Permalink
Post by Remi Forax
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).
Seems like the reflection API is at the wrong level and a Java model/mirror API might be able to handle this correctly?

Paul.
Remi Forax
2014-03-14 18:23:08 UTC
Permalink
Post by Paul Sandoz
Post by Remi Forax
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).
Seems like the reflection API is at the wrong level and a Java model/mirror API might be able to handle this correctly?
yes, it's a hack,
using a declared type (type are artifact of the compiler) at runtime (in
the VM realm) to convert a message is not very natural.

yes, it's possible to use an annotation processor for that, it's a far
better that trying to emulate the compiler job at runtime.
Now, requiring an annotation processor make the API heavier to use.
Post by Paul Sandoz
Paul.
cheers,
R?mi
Loading...