TypeToken
因為 java 有 type erasure 特性,無法在 runtime 得知 generic Class 物件
TypeToken 使用了 workaround 方法,可處理 generict types,就是能夠在 runtime 檢測 Generic Type
@Test
public void typeToken() {
ArrayList<String> stringList = Lists.newArrayList();
ArrayList<Integer> intList = Lists.newArrayList();
System.out.println(stringList.getClass().isAssignableFrom(intList.getClass()));
// returns true, even though ArrayList<String> is not assignable from ArrayList<Integer>
TypeToken<List<String>> stringListToken = new TypeToken<List<String>>() {};
TypeToken<List<Integer>> integerListToken = new TypeToken<List<Integer>>() {};
TypeToken<List<? extends Number>> numberTypeToken = new TypeToken<List<? extends Number>>() {};
assertFalse(stringListToken.isSubtypeOf(integerListToken));
assertFalse(numberTypeToken.isSubtypeOf(integerListToken));
assertTrue(integerListToken.isSubtypeOf(numberTypeToken));
}
利用 TypeToken 在 runtime 取得 complex type 資訊
abstract class ParametrizedClass<T> {
TypeToken<T> type = new TypeToken<T>(getClass()) {};
}
@Test public void typeToken2() throws NoSuchMethodException {
//// 產生一個 class 裡面儲存了 TypeToken 欄位
ParametrizedClass<String> parametrizedClass = new ParametrizedClass<String>() {};
assertEquals(parametrizedClass.type, TypeToken.of(String.class));
//// 也能產生 TypeToken of a complex type,裡面有多個 generic type
// runtime 還是能取得 type 資訊
TypeToken<Function<Integer, String>> funToken
= new TypeToken<Function<Integer, String>>() {};
TypeToken<?> funResultToken = funToken
.resolveType(Function.class.getTypeParameters()[1]);
assertEquals(funResultToken, TypeToken.of(String.class));
/// 也能在 Map 裡面得知 entry 的 data type
TypeToken<Map<String, Integer>> mapToken
= new TypeToken<Map<String, Integer>>() {};
TypeToken<?> entrySetToken = mapToken
.resolveType(Map.class.getMethod("entrySet")
.getGenericReturnType());
assertEquals(
entrySetToken,
new TypeToken<Set<Map.Entry<String, Integer>>>() {});
}
Invokable
Invokable 是 java.lang.reflec.Method 及 java.lang.reflect.Constructor 的 fluent wrapper,提供簡化後的 API
@Test
public void invokable() throws NoSuchMethodException {
Method method = CustomClass.class.getMethod("somePublicMethod");
Invokable<CustomClass, ?> invokable
= new TypeToken<CustomClass>() {}
.method(method);
// 分別透過 Guava 及 標準的 JAVA reflection API 檢測是否有 somePublicMethod 這個 method
boolean isPublicStandradJava = Modifier.isPublic(method.getModifiers());
boolean isPublicGuava = invokable.isPublic();
assertTrue(isPublicStandradJava);
assertTrue(isPublicGuava);
////////
// checking if a method is overridable
Method method2 = CustomClass.class.getMethod("notOverridablePublicMethod");
// 用 Guava TypeToken 比較簡單
Invokable<CustomClass, ?> invokable2
= new TypeToken<CustomClass>() {}.method(method2);
// 用標準 java 的方法
boolean isOverridableStandardJava = (!(Modifier.isFinal(method2.getModifiers())
|| Modifier.isPrivate(method2.getModifiers())
|| Modifier.isStatic(method2.getModifiers())
|| Modifier.isFinal(method2.getDeclaringClass().getModifiers())));
boolean isOverridableFinalGauava = invokable2.isOverridable();
assertFalse(isOverridableStandardJava);
assertFalse(isOverridableFinalGauava);
}