Пожалуйста, простите меня, если вы уже знаете большую часть ответа: на вашем уровне сложно сделать предположение.
Причина проблемы - стирание типа, как вы уже знаете.Чтобы избавиться от стирания типа, Guice использует трюк с конкретными предками, как показано ниже:
class Trick<T> {
T t;
}
public class GenericTest {
public static void main(String[] args) {
Trick<Set<String>> trick = new Trick<Set<String>>() {
};
// Prints "class org.acm.afilippov.GenericTest$1"
System.out.println(trick.getClass());
// Prints "org.acm.afilippov.Trick<java.util.Set<java.lang.String>>"
System.out.println(trick.getClass().getGenericSuperclass());
}
}
Точка, когда вы создаете класс, который расширяет универсальный суперкласс и явно указываетпараметр типа , как правило, вам нужно написать методов, которые принимают этот очень специфический тип, и сигнатуры этих методов не могут быть стерты .В этом случае у нас нет проблем, обсуждаемых в FAQ, но в любом случае компилятор сохраняет информацию о типе: пользователям вашего класса нужно будет знать точные типы, чтобы использовать методы.
Теперь ваша версия не имеет конкретного класса, унаследованного от TypeLiteral<Set<YourSpecificType>>
, она имеет только TypeLiteral<Set<T>>
- и вот где все это терпит неудачу.
Если изменить мой маленький пример, это будет:
public class GenericTest {
public static void main(String[] args) {
tryMe(String.class);
}
private static <T> void tryMe(Class<T> clazz) {
Trick<Set<T>> trick = new Trick<Set<T>>() {
};
// Prints "class org.acm.afilippov.GenericTest$1"
System.out.println(trick.getClass());
// Prints "org.acm.afilippov.Trick<java.util.Set<T>>"
System.out.println(trick.getClass().getGenericSuperclass());
}
}
Как видите, наш GenericTest$1
больше не конкретен: у него все еще есть параметр типа, и его конкретное значение, здесь String
, теряется во время компиляции.
Конечно, этого можно избежать, но для этого вам нужно создать класс с определенным параметром типа, используемым для наследования, чтобы Guice мог разобраться в деталях.Подождите немного, я попытаюсь придумать пример.
Обновление: оказалось ОЧЕНЬ длинным битом.Итак, вот обновленная версия для вас:
public class GenericTest {
public static void main(String[] args) throws Exception {
tryMe(String.class);
}
private static <T> void tryMe(Class<T> clazz) throws IllegalAccessException, InstantiationException {
Class c = loadClass("org.acm.afilippov.ASMTrick", generateClass(clazz));
Trick<Set<T>> trick = (Trick<Set<T>>) c.newInstance();
// Prints "class org.acm.afilippov.ASMTrick"
System.out.println(trick.getClass());
// Prints "org.acm.afilippov.Trick<java.util.Set<java.lang.String>>"
System.out.println(trick.getClass().getGenericSuperclass());
}
private static byte[] generateClass(Class<?> element) {
ClassWriter cw = new ClassWriter(0);
MethodVisitor mv;
cw.visit(V1_6, ACC_FINAL + ACC_SUPER, "org/acm/afilippov/ASMTrick",
"Lorg/acm/afilippov/Trick<Ljava/util/Set<L" + element.getName().replaceAll("\\.", "/") + ";>;>;",
"org/acm/afilippov/Trick", null);
{
mv = cw.visitMethod(0, "<init>", "()V", null, null);
mv.visitCode();
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, "org/acm/afilippov/Trick", "<init>", "()V");
mv.visitInsn(RETURN);
mv.visitMaxs(1, 1);
mv.visitEnd();
}
cw.visitEnd();
return cw.toByteArray();
}
private static Class loadClass(String className, byte[] b) {
//override classDefine (as it is protected) and define the class.
Class clazz = null;
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class cls = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method method =
cls.getDeclaredMethod("defineClass", new Class[]{String.class, byte[].class, int.class, int.class});
// protected method invocaton
method.setAccessible(true);
try {
Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length)};
clazz = (Class) method.invoke(loader, args);
} finally {
method.setAccessible(false);
}
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
return clazz;
}
}
Как видите, информация о типе теперь сохраняется.Я считаю, что этот подход не используется, потому что он слишком болезненный даже для этого проекта.