2015-05-12 2 views
4

У меня есть класс, который использует generics. Потому что необходима ссылка на общий тип, конструктор имеет Class параметр (как обсуждалось в this question), так это выглядит следующим образом:Двойное генерирование в Java

MyGenericClass<T> 
public MyGenericClass(final Class<T> valueClass) { 
    this.clazz = valueClass; 
} 

Теперь я хочу передать MyGenericClass как T, но я могу» t заставить его работать.

MyGenericClass<MyGenericClass<String>> foo = 
     new MyGenericClass<MyGenericClass<String>>(MyGenericClass<String>.class); 

дает мне ошибку компилятора.

Похоже, что это проблема типа Erasure (см. this SO question). С этим двойным слоем дженериков я не уверен, что можно делать то, что я хочу делать.

Любые предложения?

UTA:

Я не могу отправить весь код, потому что это корпоративный код и я не уверен, правил размещения. Класс по существу представляет собой MultipleValueMap, который отображает один ключ в список значений. Объект класса используется для создания типизированного массива значений в списке (с использованием list.toArray(T[]))

В этом случае я хочу иметь ключ, который сопоставляется с несколькими списками значений. И это вызывает проблему.

+0

Это реальный код, скопированный или ошибка ввода? Отсутствует «>» после строки и до «)». –

+0

Это зависит от того, что вы делаете с 'this.clazz'. Для чего ты это используешь? – Radiodef

+0

Вы действительно используете этот объект clazz? –

ответ

0

Я не думаю, что вы хотите использовать класс. Это ожидает объект Class.

Просто используйте T или объект <T> вместо этого, если класс. Это то, что вам кажется из вашего примера.

+0

Мне нужен объект Class, поэтому я могу создать T []. Не могу сделать это только с помощью T или Object chama

+0

Вы не можете использовать «Список»? Вы понимаете, что Generics существует только во время компиляции, правильно? Что единственная цель, которую они обслуживают, - усилить проверку типа времени компиляции? – ControlAltDel

+0

Мне нужен массив, чтобы иметь возможность делать List.toArray (T []). – chama

0

Из того, что я видел, мне кажется, что вы хотите создать экземпляр Class<MyGenericClass<String>> в некотором роде, как MyGenericClass<String>.class. Таким образом, запрещено, и вы должны использовать другой вариант: вы можете принять Class<? extends T> в качестве параметра для конструктора и создать что-то вроде внутреннего класса, который расширяет общий класс с бетонным родовыми:

private static class MGCString extends MyGenericClass<String> { 
} 

и создать экземпляр нового члена как

MyGenericClass<MyGenericClass<String>> foo = new MyGenericClass<>(MGCString.class); 

Примечание от @Radiodef: Это зависит от того, что вы делаете с создаваемым массивом. Если вы хотите разместить, например, a new GenericClass<String>(String.class) в массив, то это не сработает. (Он генерирует исключение хранилища массива.) Если вы также создаете элементы с отражением (например, через class.newInstance()), возможно, это сработает.

ПОСЛЕ UPDATE

Поскольку вы выложили обновление для вопроса, теперь я могу ответить на ваш вопрос :) Обычный способ преобразования List в массив, чтобы переместить элементы List «s в какой-то массив, который предоставляется пользователем.

Например, мы можем посмотреть List.toArray(T[]). Этот метод помещает все элементы списка в переданный массив, если он достаточно большой. Иначе он создает новый массив (да, мы можем получить его тип, запустив a.getClass()).

Кроме того, в Java 8, Streams API ввел the brand new way сделать то же самое: обеспечить генератор для T[]:

//get new Stream<String>, for example, someway 
     .toArray(String[]::new) 

Вы можете добавить эту возможность в свой код, если вы создаете какой-то метод, который принимает IntFunction<T[]> arrayGenerator и запускает arrayGenerator.apply(list.size()), чтобы получить массив запрашиваемого размера.

+0

Это зависит от того, что делает OP с создаваемым массивом. Если они хотят поставить, например, «новый GenericClass (String.class)' in to array, то это не сработает. (Он генерирует исключение хранилища массива.) Если они создают элементы с отражением, то возможно это работает. – Radiodef

+0

@Radiodef кажется законным. Может быть, я должен включить ваш комментарий в ответ. –

+0

Вы можете это сделать. Я не против. – Radiodef

3

Дженерики начинают разрушаться в такой ситуации. List<String> - тип, но не класс, поэтому существует только List.class.

Guava's TypeToken был создан для этих ситуаций.

class MyGenericClass<T> { 
    private Class<T> raw; 

    /* the code in this constructor 
    * has similar semantics to e.g. 
    * MyGenericClass<String>[] arr = new MyGenericClass[10]; 
    * when passing a 
    * new TypeToken<MyGenericClass<String>>() {} 
    */ 
    MyGenericClass(TypeToken<T> type) { 
     @SuppressWarnings("unchecked") 
     final Class<T> unchecked = (Class<T>) type.getRawType(); 
     raw = unchecked; 

     @SuppressWarnings("unchecked") 
     T[] arr = (T[]) Array.newInstance(type, 10); 
     // 
    } 
} 
MyGenericClass<MyGenericClass<String>> g = 
    new MyGenericClass<>(new TypeToken<MyGenericClass<String>>() {}); 

В противном случае ваши варианты немного ограничены. Есть запутана варианта:

MyGenericClass<MyGenericClass<String>> g = 
    new MyGenericClass<MyGenericClass<String>>(
     (Class) MyGenericClass.class); 

Также возможно изменение Class<T> к Class<? extends T> и прохождению подкласса (который я вижу, кто-то уже писал, так что я не стану повторять).

Если вам не нужно возвращать массив с внешним миром, вы могли бы сделать:

class MyGenericClass<T> { 
    private Class<? super T> maybeSuper; 

    MyGenericClass(Class<? super T> maybeSuper) { 
     this.maybeSuper = maybeSuper; 

     // array which accepts T 
     Object[] arr = (Object[]) Array.newInstance(maybeSuper, 10); 
    } 
} 

Тогда вы можете сделать:

MyGenericClass<MyGenericClass<String>> g = 
    new MyGenericClass<MyGenericClass<String>>(
     MyGenericClass.class); 

, потому что сырой тип супертипом параметризованный тип.

+0

Я не могу представить дополнительную зависимость от Guava, но спасибо за ваш ответ. – chama

+0

Ну, вы все равно можете использовать что-то вроде этого без Guava. См. Http://gafter.blogspot.com/2006/12/super-type-tokens.html. – Radiodef

0

Вы можете это сделать, вроде ..,

Ваша основная проблема заключается в том, что тип Т Class<T> не может сама по себе быть универсальным, то есть:

Class<List> a; // OK 
Class<List<String>> b; // can't do this 

Но! Вы можете обойти эту проблему путем создания типа для prepresent набранного типа, например:

interface StringList extends List<String> {} 
Class<? extends StringList> c; OK! 

Переводя это к вашей ситуации, вы должны создать интерфейс для prepresent MyGenericClass (чтобы избежать проблем конструктора в абстрактном типе), тогда есть подтипы этого и передают их в ваши конструкторы.

0

Это зависит от вашей цели.

Вы можете получить выражение типа Class<MyGenericClass<String>> делая некоторую небезопасную отливку MyGenericClass.class:

(Class<MyGenericClass<String>>)(Class<?>)MyGenericClass.class 

Но действительно ли это то, что вы хотите? Зачем нужны небезопасные броски?

В каком-то смысле, вы не можете «действительно» есть Class<MyGenericClass<String>>, потому что Class<T> может проверить во время выполнения данного объекта является ли тип T, с методом .isIntance(), но вы никогда не можете проверить аргументы универсального типа объекта во время выполнения потому что он стирается. Для некоторых целей, однако, может быть достаточно «притворяться», что MyGenericClass.class (типа Class<MyGenericClass>) является Class<MyGenericClass<String>> (это то, что делают небезопасные роли).

Так что дело доходит до того, что делает ваша функция с объектом класса. Из вашего описания вы говорите: «Объект класса используется для создания типизированного массива значений в списке (с использованием list.toArray(T[]))». Как вы знаете, создание универсального массива в Java запрещено. Поэтому, даже если вы работали с конкретным классом напрямую, а не с T, вы все равно не могли сделать new MyGenericClass<String>[42]. Лучшее, что вы можете сделать, это (MyGenericClass<String>[])new MyGenericClass[42] или (MyGenericClass<String>[])new MyGenericClass<?>[42]. Другими словами, вы создаете массив с использованием класса raw-type, а затем «притворяетесь», что созданный массив является массивом параметризованного типа, используя небезопасные отбрасывания.

Это аналогично слепков выше с классом объекта:

(Class<MyGenericClass<String>>)(Class<?>)MyGenericClass.class 

- вы используете объект класса сырым типа, а затем «притворяется», что класс имеет параметризованного типа с использованием небезопасных cast, так что когда вы создадите массив, вы подумаете, что это массив параметризованного типа.

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