RxViper { static Class getGenericParameterClass(final Class<?> actualClass, final Class<?> genericClass, final int parameterIndex) { check(genericClass.isAssignableFrom(actualClass) && !genericClass.equals(actualClass), format("Class %s is not a superclass of %s.", genericClass.getName(), actualClass.getName())); final boolean isInterface = genericClass.isInterface(); final Stack<ParameterizedType> genericClasses = new Stack<>(); Class clazz = actualClass; while (true) { final Type genericInterface = isInterface ? getGenericInterface(clazz, genericClass) : null; final Type currentType = genericInterface == null ? clazz.getGenericSuperclass() : genericInterface; final boolean isParameterizedType = currentType instanceof ParameterizedType; if (isParameterizedType) { genericClasses.push((ParameterizedType) currentType); } else { genericClasses.clear(); } final Type rawType = isParameterizedType ? ((ParameterizedType) currentType).getRawType() : currentType; if (!rawType.equals(genericClass)) { clazz = (Class) rawType; } else { break; } } Type result = genericClasses.pop() .getActualTypeArguments()[parameterIndex]; while (result instanceof TypeVariable && !genericClasses.empty()) { final int actualArgumentIndex = getParameterTypeDeclarationIndex((TypeVariable) result); final ParameterizedType type = genericClasses.pop(); result = type.getActualTypeArguments()[actualArgumentIndex]; } check(!(result instanceof TypeVariable), format("Unable to resolve type variable %s. Try to replace instances of parametrized class with its non-parameterized subtype.", result)); if (result instanceof ParameterizedType) { result = ((ParameterizedType) result).getRawType(); } check(result != null, format("Unable to determine actual parameter type for %s.", actualClass.getName())); check(result instanceof Class, format("Actual parameter type for %s is not a Class.", actualClass.getName())); return (Class) result; } private  RxViper();   }
@Test public void testGenericParameterClass() { class ClassA<S, I> {} class ClassB<I, S, C extends Collection> extends ClassA<S, I> {} class ClassC<S extends Comparable<String>, D, I> extends ClassB<I, S, Set<Long>> {} class ClassD<I, D extends Comparable<Double>> extends ClassC<String, D, I> implements InterfaceB<D, I> {} class ClassE extends ClassD<Integer, Double> {} class ClassF<B, L> extends ClassE {} class ClassG extends ClassF<Byte, Long> {} final Class<?> classG = ClassG.class; assertThat(RxViper.getGenericParameterClass(classG, ClassA.class, 1)).isNotSameAs(String.class); assertThat(RxViper.getGenericParameterClass(classG, ClassA.class, 0)).isSameAs(String.class); assertThat(RxViper.getGenericParameterClass(classG, ClassA.class, 1)).isSameAs(Integer.class); assertThat(RxViper.getGenericParameterClass(classG, ClassB.class, 0)).isSameAs(Integer.class); assertThat(RxViper.getGenericParameterClass(classG, ClassB.class, 1)).isSameAs(String.class); assertThat(RxViper.getGenericParameterClass(classG, ClassB.class, 2)).isSameAs(Set.class); assertThat(RxViper.getGenericParameterClass(classG, ClassC.class, 0)).isSameAs(String.class); assertThat(RxViper.getGenericParameterClass(classG, ClassC.class, 1)).isSameAs(Double.class); assertThat(RxViper.getGenericParameterClass(classG, ClassC.class, 2)).isSameAs(Integer.class); assertThat(RxViper.getGenericParameterClass(classG, ClassD.class, 0)).isSameAs(Integer.class); assertThat(RxViper.getGenericParameterClass(classG, ClassD.class, 1)).isSameAs(Double.class); assertThat(RxViper.getGenericParameterClass(classG, ClassF.class, 0)).isSameAs(Byte.class); assertThat(RxViper.getGenericParameterClass(classG, ClassF.class, 1)).isSameAs(Long.class); assertThat(RxViper.getGenericParameterClass(classG, InterfaceA.class, 0)).isSameAs(Integer.class); assertThat(RxViper.getGenericParameterClass(classG, InterfaceB.class, 0)).isSameAs(Double.class); assertThat(RxViper.getGenericParameterClass(classG, InterfaceB.class, 1)).isSameAs(Integer.class); thrown.expect(EmptyStackException.class); assertThat(RxViper.getGenericParameterClass(classG, ClassE.class, 0)).isSameAs(Double.class); }
@Test public void shouldNotGetTypeSelf() { thrown.expect(IllegalStateException.class); RxViper.getGenericParameterClass(Object.class, Object.class, 0); }