Skip to content

Instantly share code, notes, and snippets.

@phantamanta44
Created August 9, 2019 19:11
Show Gist options
  • Save phantamanta44/436619e00b4abea67356668182fc57d0 to your computer and use it in GitHub Desktop.
Save phantamanta44/436619e00b4abea67356668182fc57d0 to your computer and use it in GitHub Desktop.
/**
* Represents a parameterized type. Unfortunately necessary because the JVM doesn't support
* reified generics in all cases.
*/
@SuppressWarnings("unchecked")
public class TypeToken<T> {
/**
* The type represented by this type token.
*/
public final Class<T> type;
/**
* The type parameters to the type represented by this type token.
*/
public final TypeToken<?>[] parameters;
/**
* Creates a new type token for the given type, parameterized with the given type parameters.
*/
public TypeToken(Class<T> type, TypeToken<?>... parameters) {
this.type = type;
this.parameters = parameters;
}
/**
* Attempts to create a type token by using the JVM's limited generic reification capabilities.
* This constructor must be invoked from a subtype to work!
*/
protected TypeToken() {
Type thisType = getClass().getGenericSuperclass();
try {
Type tp = ((ParameterizedType)thisType).getActualTypeArguments()[0];
if (tp instanceof ParameterizedType) {
ParameterizedType ptp = (ParameterizedType)tp;
this.type = (Class<T>)ptp.getRawType();
this.parameters = Arrays.stream(ptp.getActualTypeArguments())
.map(TypeToken::new).toArray(TypeToken[]::new);
} else {
this.type = (Class<T>)tp;
this.parameters = new TypeToken[0];
}
} catch (Exception e) {
throw new UnsupportedOperationException("Could not compute parameterized type for " + thisType, e);
}
}
/**
* Attempts to create a type token from a reified generic type, assuming all its parameters are also reified.
*/
private TypeToken(Type tp) {
try {
if (tp instanceof ParameterizedType) {
ParameterizedType ptp = (ParameterizedType)tp;
this.type = (Class<T>)ptp.getRawType();
this.parameters = Arrays.stream(ptp.getActualTypeArguments())
.map(TypeToken::new).toArray(TypeToken[]::new);
} else {
this.type = (Class<T>)tp;
this.parameters = new TypeToken[0];
}
} catch (Exception e) {
throw new UnsupportedOperationException("Could not compute parameterized type for " + tp, e);
}
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder(type.getSimpleName());
if (parameters.length > 0) {
buf.append("<").append(parameters[0].toString());
for (int i = 1; i < parameters.length; i++) {
buf.append(", ").append(parameters[i].toString());
}
buf.append(">");
}
return buf.toString();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment