Как создать литерал класса известного типа:Класс<Список<Строка>>
Вопрос
Возьмите следующее:
public Class<List<String>> getObjectType() {
// what can I return here?
}
Какое буквальное выражение класса я могу вернуть из этого метода, чтобы оно удовлетворяло дженерикам и компилировалось? List.class
не скомпилируется и не будет List.<String>class
.
Если вам интересно «почему», я пишу реализацию Spring FactoryBean<List<String>>
, что требует от меня реализации Class<List<String>> getObjectType()
.Однако это нет Весенний вопрос.
редактировать: Мои жалобные крики были услышаны властями SpringSource, и поэтому Spring 3.0.1 будет иметь возвращаемый тип getObjectType()
изменился на Class<?>
, что аккуратно позволяет избежать проблемы.
Решение
Вы всегда можете использовать то, что вам нужно, вот так
return (Class<List<String>>) new ArrayList<String>().getClass();
или
return (Class<List<String>>) Collections.<String>emptyList().getClass();
Но я предполагаю, что это не то, что вам нужно.Ну, это работает, с предупреждением, но это не совсем «красиво».
Я только что нашел это
Почему для типов с параметризованными подстановочными знаками не существует литерала класса?
Потому что параметризованный тип с подстановочными знаками не имеет точного представления типа во время выполнения.
Так что кастинг может быть единственным выходом.
Другие советы
Никогда не следует использовать конструкцию Class<List<String>>
.Это бессмысленно и должно выдавать предупреждение на Java (но этого не происходит).Экземпляры классов всегда представляют необработанные типы, поэтому вы можете Class<List>
;вот и все.Если вы хотите, чтобы что-то представляло конкретизированный универсальный тип, например List<String>
, вам нужен «токен супертипа», который использует Guice:
http://google-guice.googlecode.com/git/javadoc/com/google/inject/TypeLiteral.html
Существование Class<List<String>>
по своей сути опасен.вот почему:
// This statement generates a warning - for a reason...
Class<List<String>> unsafeListClass = (Class<List<String>>) (Class<?>) List.class;
List<Integer> integerList = new ArrayList<Integer>(); // Ok
integerList.add(42); // Ok
System.out.println(unsafeListClass.isInstance(integerList)); // Prints "true".
List<String> stringList =
unsafeListClass.cast(integerList); // Succeeds, with no warning!
stringList.add("Hello, World!"); // Also succeeds with no warning
for (int x: integerList) {
// Compiles without warning, but throws ClassCastException at runtime
System.out.println(100-x);
}
Найденный эта ссылка на Springframework.org, что дает некоторое представление.
Например.
List<String> myList = new ArrayList<String>();
return (Class<List<String>>)myList.getClass();
Вы можете реализовать этот метод следующим образом:
public Class<List<String>> getObjectType() {
return (Class<List<String>>) ((Class)List.class);
}
Посмотрите это обсуждение на форумах SUN:
http://forums.sun.com/thread.jspa?threadID=5253007
И упомянутое сообщение в блоге, в котором описывается обходной путь с использованием «токенов супертипа»:
Я не уверен, возможно ли это вообще, поскольку любой литерал класса будет скомпилирован в Class.forName(...)
и поскольку это происходит во время выполнения, общей информации не остается.
Я не уверен, но следующий вопрос, который я задал, может быть вам интересен...
Как насчет этого:
public class TestMain {
public static void main(String[] args) throws Exception {
Type type = TestMain.class.getMethod("dummy").getGenericReturnType();
System.out.println("type = " + type);
}
public List<Integer> dummy() {return null;}
}
Это печатает:
type = java.util.List<java.lang.Integer>
Следующий подход проблематичен:
> public Class<List<String>> getModelType() {
> return (Class<List<String>>) new ArrayList<String>().getClass();
> }
напримересли вы хотите проверить, имеет ли объект тип
org.eclipse.emf.common.util.BasicEList<String>
имеет тип
List<String>
на основе результата вышеупомянутого подхода getModelType(), например:
BasicEList<String> fromObject = ...;
if (getModelType().isAssignableFrom(fromObject.getClass())) {
transferFromModelToUi(getModelType().cast(fromObject));
}
это приведет к ложному результату, тогда как должно быть истинным, поскольку оба объекта реализуют интерфейс List (поскольку getModelType() возвращает объект Class типа List, а не ArrayList).
Вот подход, который сработал для меня (немного громоздкий, но приводит к правильным результатам в приведенном выше примере, его можно переместить в статический инициализатор):
public Class<List<String>> getModelType() {
Class<?> arrayListClass = new ArrayList<String>().getClass();
Class<?>[] interfaces = arrayListClass.getInterfaces();
int index = 0;
for (int i = 0; i < interfaces.length; i++) {
if (interfaces[i].equals(List.class)) {
index = i;
break;
}
}
return (Class<List<String>>) interfaces[index];
}