Add some reflection with generics experiments

This commit is contained in:
Thibaud Gasser 2018-12-28 19:18:47 +01:00
parent 5c7e822b53
commit 35cc5abdf0
6 changed files with 158 additions and 4 deletions

View File

@ -9,7 +9,7 @@
</parent> </parent>
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<artifactId>core</artifactId> <artifactId>lang</artifactId>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>junit</groupId> <groupId>junit</groupId>

View File

@ -10,7 +10,7 @@
<module>reflection</module> <module>reflection</module>
<module>gui</module> <module>gui</module>
<module>async</module> <module>async</module>
<module>core</module> <module>lang</module>
</modules> </modules>
<properties> <properties>

View File

@ -1,4 +1,4 @@
package fr.gasser.reflection.dynamicload; package fr.gasser.reflection;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.ToolProvider; import javax.tools.ToolProvider;
@ -18,7 +18,7 @@ public class DynamicLoadDemo {
*/ */
private static final String SOURCE = private static final String SOURCE =
"package test;" "package test;"
+ "import static fr.gasser.reflection.dynamicload.DynamicLoadDemo.*;" + "import static DynamicLoadDemo.*;"
+ "public class Test implements IDynamicLoad {" + "public class Test implements IDynamicLoad {"
+ " static { System.out.println(\"Test\"); }" + " static { System.out.println(\"Test\"); }"
+ " @Override public String doSomething() {" + " @Override public String doSomething() {"

View File

@ -0,0 +1,26 @@
package fr.gasser.reflection;
import java.lang.reflect.ParameterizedType;
/**
* Shows how to access generic type parameter type at runtime.
*/
public abstract class GenericClass<T> {
private Class<T> clazz;
@SuppressWarnings("unchecked")
private GenericClass() {
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
this.clazz = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
}
Class<T> getClazz() {
return clazz;
}
public static void main(String[] args) {
System.out.println(new GenericClass<Long>() {}.getClazz());
System.out.println(new GenericClass<Integer>() {}.getClazz());
}
}

View File

@ -0,0 +1,26 @@
package fr.gasser.reflection;
public class GenericsTest<T> {
private TypeToken<T> typeToken;
private T data;
public GenericsTest(T data) {
this.data = data;
this.typeToken = new TypeToken<T>() {};
}
@Override
public String toString() {
return "GenericsTest{" +
"type=" + typeToken.type +
", data=" + data +
'}';
}
public static void main(String[] args) {
GenericsTest<Long> g = new GenericsTest<>(5L);
System.out.println(g);
}
}

View File

@ -0,0 +1,102 @@
package fr.gasser.reflection;
import java.lang.reflect.*;
import java.util.List;
/*
* {@code TypeToken<List<String>> list = new TypeToken<List<String>>() {};}
*/
public class TypeToken<T> {
private final Class<? super T> rawType;
final Type type;
private final int hashCode;
@SuppressWarnings("unchecked")
TypeToken() {
this.type = getSuperclassTypeParameter(getClass());
this.rawType = (Class<? super T>) getRawType(type);
this.hashCode = type.hashCode();
}
/**
* Unsafe. Constructs a type literal manually.
*/
@SuppressWarnings("unchecked")
private TypeToken(Type type) {
this.type = checkNotNull(type);
this.rawType = (Class<? super T>) getRawType(this.type);
this.hashCode = this.type.hashCode();
}
/**
* Returns the type from super class's type parameter
*/
private static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superclass = subclass.getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
return parameterized.getActualTypeArguments()[0];
}
public static Class<?> getRawType(Type type) {
if (type instanceof Class<?>) {
// type is a normal class.
return (Class<?>) type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class.
// Neal isn't either but suspects some pathological case related
// to nested classes exists.
Type rawType = parameterizedType.getRawType();
checkArgument(rawType instanceof Class);
return (Class<?>) rawType;
} else if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType)type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
} else if (type instanceof TypeVariable) {
// we could use the variable's bounds, but that won't work if there are multiple.
// having a raw type that's more general than necessary is okay
return Object.class;
} else if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
} else {
String className = type == null ? "null" : type.getClass().getName();
throw new IllegalArgumentException("Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <" + type + "> is of type " + className);
}
}
private static void checkArgument(boolean condition) {
if (!condition) {
throw new IllegalArgumentException();
}
}
private static <T> T checkNotNull(T obj) {
if (obj == null) {
throw new NullPointerException();
}
return obj;
}
/**
* Gets type literal for the given {@code Class} instance.
*/
public static <T> TypeToken<T> get(Class<T> type) {
return new TypeToken<T>(type);
}
public static void main(String[] args) {
TypeToken<List<String>> token = new TypeToken<List<String>>() {};
System.out.println(token);
}
}