package tap.util;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
/**
* Represents a generic type {@code T}.
*
* Neil Gafter's "Super Type Token" implementation. Taken from
* <a href="http://gafter.blogspot.com/2006/12/super-type-tokens.html">here</a> and
* <a href="http://gafter.blogspot.com/2007/05/limitation-of-super-type-tokens.html">here</a>.
*
* The class is abstract so that the user is required to create a subclass, typically an anonymous one via
* TypeRef<MyClass> typeRef = new TypeRef<MyClass>(){};
* Note the {} at the end, which makes this an anonymous subclass. Then calling getGenericSuperclass
* in the constructor returns the class in this file, from which we can inspect the type parameter.
* See <a href="http://gafter.blogspot.com/2004/09/puzzling-through-erasure-answer.html">here</a> for
* more about type erasure in Java.
*/
public abstract class TypeRef<T> {
private final Type type_;
private volatile Class class_;
private volatile Constructor<?> constructor_;
/**
* Constructs a new generic type, deriving the generic type and class from
* type parameter. Note that this constructor is protected, users should create
* a (usually anonymous) subclass as shown above.
*
*/
protected TypeRef() {
Type superclass = getClass().getGenericSuperclass();
if (!(superclass instanceof ParameterizedType)) {
throw new RuntimeException("Missing type parameter.");
}
ParameterizedType parameterized = (ParameterizedType) superclass;
type_ = parameterized.getActualTypeArguments()[0];
}
/**
* Constructs a new generic type, supplying the generic type
* information and derving the class.
*
* @param genericType the generic type.
* @throws IllegalArgumentException if genericType
* is null or is neither an instance of Class or ParameterizedType whose raw
* type is not an instance of Class.
*/
public TypeRef(Type genericType) {
if (genericType == null) {
throw new IllegalArgumentException("Type must not be null");
}
type_ = genericType;
}
private static Class getClass(Type type) {
if (type instanceof Class) {
return (Class)type;
} else if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType)type;
if (parameterizedType.getRawType() instanceof Class) {
return (Class)parameterizedType.getRawType();
}
}
throw new IllegalArgumentException("Type parameter [" + type.toString() + "] not a class or " +
"parameterized type whose raw type is a class");
}
/**
* Use the typeRef's parameter to create a new instance of the TypeRef's template parameter.
* @return a new instance of type T
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
@SuppressWarnings("unchecked")
public T newInstance()
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (constructor_ == null) {
constructor_ = getRawClass().getConstructor();
}
return (T)constructor_.newInstance();
}
/**
* The cheap, ugly version of the above, for when you don't want to catch 900 exceptions at
* the calling site.
* @return a new instance of type T.
*/
public T safeNewInstance() {
try {
return newInstance();
} catch (NoSuchMethodException e) {
throw new IllegalArgumentException(e);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException(e);
} catch (InvocationTargetException e) {
throw new IllegalArgumentException(e);
} catch (InstantiationException e) {
throw new IllegalArgumentException(e);
}
}
/**
* Gets underlying {@code Type} instance derived from the
* type.
* @return the type.
*/
public final Type getType() {
return type_;
}
/**
* Gets underlying raw class instance derived from the
* type.
* @return the class.
*/
public final Class<T> getRawClass() {
if (class_ == null) {
class_ = getClass(type_);
}
return class_;
}
}