Types { public static boolean isAssignableFrom(Type t1, Type t2) { checkNotNull(t1); checkNotNull(t2); if (t1.equals(t2)) { return true; } if (t1 instanceof Class<?>) { return ((Class<?>) t1).isAssignableFrom(getRawType(t2)); } if (t1 instanceof ParameterizedType) { Class<?> rawTypeT1 = getRawType(t1); Class<?> rawTypeT2 = getRawType(t2); if (!rawTypeT1.isAssignableFrom(rawTypeT2) || rawTypeT1.equals(rawTypeT2)) { return false; } if (t2 instanceof Class<?>) { Type genericSuperclass = ((Class) t2).getGenericSuperclass(); if (genericSuperclass != null) { if (isAssignableFrom(t1, genericSuperclass)) { return true; } } Type[] genericInterfaces = ((Class) t2).getGenericInterfaces(); for (Type genericInterface : genericInterfaces) { if (isAssignableFrom(t1, genericInterface)) { return true; } } return false; } if (t2 instanceof ParameterizedType) { Type[] actualTypeArguments = ((ParameterizedType) t2).getActualTypeArguments(); TypeVariable<? extends Class<?>>[] typeParameters = rawTypeT2.getTypeParameters(); Map<TypeVariable<? extends Class<?>>, Type> typesMap = Maps.newHashMapWithExpectedSize(typeParameters.length); for (int i = 0; i < typeParameters.length; i++) { typesMap.put(typeParameters[i], actualTypeArguments[i]); } Type genericSuperclass = rawTypeT2.getGenericSuperclass(); if (genericSuperclass != null) { if (genericSuperclass instanceof Class<?>) { if (isAssignableFrom(t1, genericSuperclass)) { return true; } } else if (genericSuperclass instanceof ParameterizedType) { ParameterizedType genericSuperclassParameterized = (ParameterizedType) genericSuperclass; Type[] genericSuperclassActualTypeArguments = genericSuperclassParameterized.getActualTypeArguments(); for (int i = 0; i < genericSuperclassActualTypeArguments.length; i++) { if (genericSuperclassActualTypeArguments[i] instanceof TypeVariable) { genericSuperclassActualTypeArguments[i] = typesMap.get(genericSuperclassActualTypeArguments[i]); } } Type genericSuperclassRawType = genericSuperclassParameterized.getRawType(); if (isAssignableFrom(t1, Types.newParameterizedType((Class<?>) genericSuperclassRawType, genericSuperclassActualTypeArguments))) { return true; } } } Type[] genericInterfaces = rawTypeT2.getGenericInterfaces(); for (Type genericInterface : genericInterfaces) { if (genericInterface instanceof Class<?>) { if (isAssignableFrom(t1, genericInterface)) { return true; } } else if (genericInterface instanceof ParameterizedType) { ParameterizedType genericInterfaceParameterized = (ParameterizedType) genericInterface; Type[] genericInterfaceActualTypeArguments = genericInterfaceParameterized.getActualTypeArguments(); for (int i = 0; i < genericInterfaceActualTypeArguments.length; i++) { if (genericInterfaceActualTypeArguments[i] instanceof TypeVariable) { genericInterfaceActualTypeArguments[i] = typesMap.get(genericInterfaceActualTypeArguments[i]); } } Type genericInterfaceRawType = genericInterfaceParameterized.getRawType(); if (isAssignableFrom(t1, Types.newParameterizedType((Class<?>) genericInterfaceRawType, genericInterfaceActualTypeArguments))) { return true; } } } return false; } } return false; }  static ParameterizedType newParameterizedType(final Class<?> rawType, final Type... arguments); static Class<?> getRawType(Type type); static boolean isAssignableFrom(Type t1, Type t2);  }
@Test public void isAssignableFrom_should_return_true_if_types_are_equals() { softly.assertThat(isAssignableFrom(String.class, String.class)).isTrue(); softly.assertThat(isAssignableFrom(int.class, int.class)).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<List<String>>() {}.getType(), new TypeReference<List<String>>() {}.getType()) ).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<List<String>[]>() {}.getType(), new TypeReference<List<String>[]>() {}.getType()) ).isTrue(); softly.assertThat(isAssignableFrom(String.class, Number.class)).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<List<String>>() {}.getType(), new TypeReference<Map<String, Integer>>() {}.getType()) ).isFalse(); }
@Test public void isAssignableFrom_should_work_with_raw_types() { softly.assertThat(isAssignableFrom(CharSequence.class, String.class)).isTrue(); softly.assertThat(isAssignableFrom(Number.class, Integer.class)).isTrue(); softly.assertThat(isAssignableFrom(List.class, ArrayList.class)).isTrue(); softly.assertThat(isAssignableFrom(Map.class, ArrayList.class)).isFalse(); softly.assertThat(isAssignableFrom(Integer.class, Number.class)).isFalse(); }
@Test public void isAssignableFrom_should_return_true_for_a_reifiable_type_and_any_of_the_same_type_parameterized() { softly.assertThat(isAssignableFrom( List.class, new TypeReference<List<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( List.class, new TypeReference<List<Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( Map.class, new TypeReference<List<String>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( Map.class, new TypeReference<Map<String, Integer>>() {}.getType() )).isTrue(); }
@Test public void isAssignableFrom_should_return_true_for_a_reifiable_type_and_any_sub_parameterized_type() { softly.assertThat(isAssignableFrom( List.class, new TypeReference<ArrayList<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( List.class, new TypeReference<AbstractList<Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( Map.class, new TypeReference<ImmutableList<String>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( Map.class, new TypeReference<ImmutableMap<String, Integer>>() {}.getType() )).isTrue(); }
@Test public void isAssignableFrom_should_work_for_parameterized_interfaces_and_a_direct_implementation() { softly.assertThat(isAssignableFrom( new TypeReference<List<String>>() {}.getType(), new TypeReference<AbstractList<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<UnTypedImpl<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<UnTypedImpl<Integer>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<Map<String, Number>>() {}.getType(), new TypeReference<AbstractMap<String, Number>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<Map<String, Number>>() {}.getType(), new TypeReference<AbstractMap<Double, Number>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<Map<String, Number>>() {}.getType(), new TypeReference<AbstractMap<String, Integer>>() {}.getType() )).isFalse(); }
@Test public void isAssignableFrom_should_work_for_parameterized_class_and_a_direct_sub_class() { softly.assertThat(isAssignableFrom( new TypeReference<AbstractList<String>>() {}.getType(), new TypeReference<ArrayList<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<AbstractList<String>>() {}.getType(), new TypeReference<ArrayList<Number>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<UnTypedImpl<String>>() {}.getType(), new TypeReference<MoreUnTypedImpl<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<UnTypedImpl<String>>() {}.getType(), new TypeReference<MoreUnTypedImpl<Integer>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<AbstractMap<String, Number>>() {}.getType(), new TypeReference<HashMap<String, Number>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<AbstractMap<String, Number>>() {}.getType(), new TypeReference<HashMap<Double, Number>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<AbstractMap<String, Number>>() {}.getType(), new TypeReference<HashMap<String, Integer>>() {}.getType() )).isFalse(); }
@Test public void isAssignableFrom_should_work_with_not_direct_sub_types() { softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<MoreUnTypedImpl<String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<MoreUnTypedImpl<Integer>>() {}.getType() )).isFalse(); }
@Test public void isAssignableFrom_should_work_with_fixed_generic_interface_implementation() { softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), StringTypedImpl.class )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Integer>>() {}.getType(), StringTypedImpl.class )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), MoreStringTypedImpl.class )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Integer>>() {}.getType(), MoreStringTypedImpl.class )).isFalse(); softly.assertThat(isAssignableFrom( StringTypedImpl.class, MoreStringTypedImpl.class )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<GenericStringTypedImpl<Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Integer>>() {}.getType(), new TypeReference<GenericStringTypedImpl<Integer>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<MoreIntegerGenericStringTypedImpl<Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Integer>>() {}.getType(), new TypeReference<MoreIntegerGenericStringTypedImpl<Integer>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<GenericStringTypedImpl<Integer>>() {}.getType(), new TypeReference<MoreIntegerGenericStringTypedImpl<Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericStringTypedImpl<Integer>>() {}.getType(), new TypeReference<MoreIntegerGenericStringTypedImpl<Double>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericStringTypedImpl<String>>() {}.getType(), new TypeReference<MoreIntegerGenericStringTypedImpl<Double>>() {}.getType() )).isFalse(); }
@Test public void isAssignableFrom_should_manage_to_match_generic_variable_from_sub_types_to_super_types() { softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Double>>() {}.getType(), new TypeReference<SomethingMap<Double, Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<SomethingMap<String, Integer>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<SomethingMap<Number, Integer>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Double>>() {}.getType(), new TypeReference<SuperMap<Integer, Double>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<SuperMap<Integer, String>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<String>>() {}.getType(), new TypeReference<SuperMap<Integer, Number>>() {}.getType() )).isFalse(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Double>>() {}.getType(), new TypeReference<FixedSuperMap<Integer, Long>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Double>>() {}.getType(), new TypeReference<FixedSuperMap<Number, Double>>() {}.getType() )).isTrue(); softly.assertThat(isAssignableFrom( new TypeReference<GenericInterface<Integer>>() {}.getType(), new TypeReference<FixedSuperMap<Number, Double>>() {}.getType() )).isFalse(); }