FULL PRODUCT VERSION :
java version "1.8.0_121"
Java(TM) SE Runtime Environment (build 1.8.0_121-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux omen 4.8.14-200.fc24.x86_64 #1 SMP Mon Dec 12 16:31:37 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
Unlike generically typed anonymous classes, generically typed lambda expressions do not preserve type variable information at runtime. For example,
public interface ContextResolver<T> {
T getContext(Class<?> type);
}
@Test
public void test() {
ContextResolver<String> resolver = ((ContextResolver<String>)type -> "foo");
for (Type type : resolver.getClass().getGenericInterfaces()) {
System.out.println("type: " + type);
}
}
outputs
type: interface javax.ws.rs.ext.ContextResolver
but running
@Test
public void test() {
ContextResolver<String> resolver1 = new ContextResolver<String>() {
@Override
public String getContext(Class<?> type) {
return null;
}
};
for (Type type : resolver.getClass().getGenericInterfaces()) {
System.out.println("type: " + type);
}
}
outputs
type: javax.ws.rs.ext.ContextResolver<java.lang.String>
There is a known solution to this problem, found here: https://github.com/jhalterman/typetools (specifically, in TypeResolver, here: https://github.com/jhalterman/typetools/blob/master/src/main/java/net/jodah/typetools/TypeResolver.java).
One problem seems to be the reference to a java.sun class:
String constantPoolName = JAVA_VERSION < 9 ? "sun.reflect.ConstantPool" : "jdk.internal.reflect.ConstantPool";
Note that the fix works for
* java-1.8.0-openjdk-1.8.0.121-8.b14.fc24.x86_64
* jdk1.8.0_121 (oracle: build 25.121-b13)
* openjdk 9 (build 9-ea+163)
but it doesn't work for
* ibm JRE 1.8.0 (build 2.8)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Just run the example above.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
javax.ws.rs.ext.ContextResolver<java.lang.String>
ACTUAL -
type: interface javax.ws.rs.ext.ContextResolver
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
public class ContextResolverTest {
public static void main(String[] args) {
ContextResolver<String> resolver = ((ContextResolver<String>)type -> "foo");
for (Type type : resolver.getClass().getGenericInterfaces())
{
System.out.println("type: " + type);
}
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
With some JDKs, it's possible to use the above mentioned TypeResolver to retrieve the value of type variables at runtime:
@Test
public void test()
{
MapFunction<String, Integer> fn = str -> Integer.valueOf(str);
Class<?>[] typeArgs = TypeResolver.resolveRawArguments(MapFunction.class, fn.getClass());
for (Class<?> clazz : typeArgs)
{
System.out.println(clazz);
}
}
outputs
class java.lang.String
class java.lang.Integer