2014-12-02 2 views
0

Я пытаюсь выделить магию Guava TypeToken для класса, который параметризуется параметром родового типа, но я получаю ClassNotFoundException при попытке создайте экземпляр метода Class.forName(String).Как создать экземпляр объекта класса для параметризованного класса с использованием отражения и Guava TypeToken

public class EnumeratedMenu<E extends Enum<E>,T extends EnumeratedBean<E>> { 

    private final TypeToken<EnumeratedMenu<E,T>> typeToken = 
      new TypeToken<EnumeratedMenu<E,T>>(getClass()) { }; 

    private Class<T> getBean() { 

     TypeToken<?> genericBeanParam = typeToken.resolveType(EnumeratedMenu.class.getTypeParameters()[1]); 

     try { 
      //This generates a ClassNotFoundException because of the generic type parameter that EnumeratedBean has. 
      return (Class<T>)Class.forName(genericBeanParam.getType().getTypeName()); 
     } catch(ClassNotFoundException e) { 
      log.error("Unable to find the class definition for {}", genericBeanParam.getType().getTypeName()); 
      log.catching(e); 
     } 
    } 
} 

Я получаю это исключение:

«Не удалось найти определение класса для EnumeratedBean<ContextMenuOption$ContextType>»

ContextMenuOption$ContextType тип выполнения родового enum параметра для EnumeratedBean. Как получить объект класса, который я хочу?

Разъяснение:

Мне нужно построить и экземпляр класса для использования с объектом строитель, который построит пункт меню, как это показано ниже:

T optionBean = beanFactory.create(beanClass); 

IEnumeratedMenuOptionBuilder<E,T> builder = new EnumeratedMenuOptionBuilder<>(optionBean); 
builder.setDriver(getDriver()).setTimeoutInSeconds(getTimeoutInSeconds()) 
     .setEnumerationFunction(getEnumerationFunction()); 

ответ

1

Это невозможно, так как всегда есть только один Class, независимо от параметризации. Вот как работают дженерики Java с типом стирания. Для детального объяснения того, как и почему, вы можете прочитать:

http://www.angelikalanger.com/GenericsFAQ/FAQSections/TechnicalDetails.html

Но чтобы Инстанцирование вам просто нужно использовать Class у вас уже есть; и нарисуйте подпись общего типа так, как вы хотите.

Это означает, например, что вы можете сделать:

List<String> strings = new ArrayList<String>(); 
List<Integer> numbers = (List<Integer>)(List<?>) strings; 
numbers.add(Integer.valueOf(12)); 

без исключений во время выполнения. Не существует реального содержимого, ограничивающего информацию о времени выполнения; только неявное литье.Вы, однако, получить исключение во время выполнения, если вы пробовали:

String str = strings.get(0); 

, потому что в то время как основной List радостно принимает любые java.lang.Object с, код доступа имеет «скрытый» бросок, который будет инициировать исключение.

Таким образом, создание может работать что-то вроде:

List<String> s = (List<String>) ArrayList.class.newInstance(); 

но используя любой обобщенный тип вы хотите.

EDIT:

И если гуавы-х TypeToken необходимо, его Javadocs советует вам либо получить сырой класс с getRawType(), или, возможно, непосредственно создать экземпляр с constructor().

+0

Спасибо. Это поставило меня на правильный путь, чтобы заставить это работать. – Selena

4

Что вы на самом деле пытаетесь сделать здесь ?

Вы никогда не будете иметь возможность получить Class<T> объект, представляющий собой общий тип, как EnumeratedBean<ContextMenuOption$ContextType>, потому что Class может представлять только сырой тип, как EnumeratedBean.

Возможно, есть другой способ добиться желаемого результата, но неясно, какой именно результат вы хотите в настоящее время. Например, что вы хотите использовать Class, к которому вы возвращаетесь? Возможно, можно сделать , что без промежуточного уровня Class объект.

Edit:

Поскольку это выглядит как то, что вы хотели, чтобы иметь возможность создать экземпляр T, вот пример того, как вы могли бы сделать это с помощью TypeToken:

public class EnumeratedMenu<E extends Enum<E>, T extends EnumeratedBean<E>> { 

    // Just get the type of T, that's what we care about here 
    private final TypeToken<T> typeToken = new TypeToken<T>(getClass()) {}; 

    private T create() { 
    try { 
     // Using the raw type Class object for the token to 
     // get at its default constructor. 
     return typeToken.constructor(typeToken.getRawType().getConstructor()) 
      .invoke(null); 
    } catch (Exception e) { 
     // ... 
    } 
    } 
} 
+0

Я пытаюсь создать объект как параметризованный тип и установить его поля, используя класс строителя, который будет пользовательскими значениями в полях bean's, чтобы построить опцию меню. – Selena

+0

@ Селена. Объект «Класс» никогда не меняется между типичными типами: фактический «Класс» для, скажем, «Список » - это точно такой же, как у «List » (не стесняйтесь проверить это, чтобы убедиться, что фактический 'Class' объект, который вы получаете через 'list.getClass()' тот же). Это также означает, что инстанцирование через отражение точно такое же; многие из типичных типов обрабатываются приложением компиляции, и нет реальной разницы во времени выполнения. Это означает, что вы не сможете создавать разные классы: их не существует. Вам скорее понадобится использовать существующий тип «Класс». – StaxMan

+0

@StaxMan: Хорошо. Итак, как мне получить экземпляр класса стираемого типа из токена типа Guava? – Selena

Смежные вопросы