Создание экземпляра типа generics в java
дубликат: Java generics почему это не будет работать
Я хотел бы создать объект типа Generics в java. Пожалуйста, предложите как я могу достичь того же.
Примечание: это может показаться тривиальной проблемой дженериков. Но держу пари.. это не так. :)
предположим, у меня есть объявление класса, как:
public class Abc<T> {
public T getInstanceOfT() {
// I want to create an instance of T and return the same.
}
}
12 ответов:
public class Abc<T> { public T getInstanceOfT(Class<T> aClass) { return aClass.newInstance(); } }
вам придется добавить обработку исключений.
вы должны передать фактический тип во время выполнения, так как он не является частью байтового кода после компиляции, поэтому нет способа узнать его без явного предоставления.
в коде, который вы опубликовали, невозможно создать экземпляр
T
так как вы не знаете, что это за тип:public class Abc<T> { public T getInstanceOfT() { // There is no way to create an instance of T here // since we don't know its type } }
конечно, это возможно, если у вас есть ссылка на
Class<T>
иT
имеет конструктор по умолчанию, просто позвонитеnewInstance()
на
то, что вы написали, не имеет никакого смысла,дженериков в Java предназначены для добавления функциональности параметрический полиморфизм к объектам.
что это значит? Это означает, что вы хотите сохранить некоторые переменные типа из ваших классов не определились, чтобы иметь возможность использовать свои классы со многими различными типами.
но ваш переменной типа
T
- это атрибут, который решается во время выполнения в Java компилятор скомпилирует ваш класс, доказывающий безопасность типов, не пытаясь узнать, какой объектT
Так что это невозможно для него, чтобы ваши использовать переменной типа в статическом методе. Тип связан с экземпляром объекта во время выполнения whilepublic void static main(..)
связан с определением класса и в этой областиT
ничего не значит.если вы хотите использовать переменной типа внутри статического метода, вы должны объявить метод как общий (это потому что, как объяснил переменные типа класса шаблона связаны с его во время выполнения экземпляра), а не класс:
class SandBox { public static <T> void myMethod() { T foobar; } }
это работает, но конечно не с
main
метод, так как нет никакого способа, чтобы вызвать его в общем виде.EDIT: проблема в том, что из-за стирания типа только один универсальный класс компилируется и передается в JVM. Type checker просто проверяет, безопасен ли код, а затем так как он доказал это каждый вид Общая информация отбрасывается.
для создания
T
вы должны знать типT
, но это может быть много типов одновременно, поэтому одно решение с требует только минимального количества отражения, чтобы использоватьClass<T>
для создания новых объектов:public class SandBox<T> { Class<T> reference; SandBox(Class<T> classRef) { reference = classRef; } public T getNewInstance() { try { return reference.newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) { SandBox<String> t = new SandBox<String>(String.class); System.out.println(t.getNewInstance().getClass().getName()); } }
конечно, это означает, что тип, который вы хотите создать:
- - это не примитивный тип
- он имеет конструктор по умолчанию
чтобы работать с различными конструкторами, вам нужно копать глубже в отражение.
вам нужно получить информацию о типе статически. Попробуйте это:
public class Abc<T> { private Class<T> clazz; public Abc(Class<T> clazz) { this.clazz = clazz; } public T getInstanceOfT() throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } }
используйте его как таковой:
Abc<String> abc = new Abc<String>(String.class); abc.getInstanceOfT();
в зависимости от ваших потребностей, вы можете использовать
Class<? extends T>
вместо.
единственный способ заставить его работать-это использовать Овеществленные Дженериков. И это не поддерживается в Java (пока? это было планируется для Java 7, но был отложен). Например, в C# поддерживается предположение, что
T
есть по умолчанию конструктор. Вы даже можете получить тип времени выполнения поtypeof(T)
и получить конструкторы поType.GetConstructor()
. Я не делаю C# , поэтому синтаксис может быть недействительным, но он примерно выглядит так это:public class Foo<T> where T:new() { public void foo() { T t = new T(); } }
лучшим "обходным путем" для этого в Java является передача
Class<T>
как аргумент метода вместо того, как уже указывалось несколько ответов.
во-первых, вы не можете получить доступ к параметру типа
T
в статическом основном методе, только на нестатических членов класса (в данном случае).во-вторых, вы не можете инстанцировать
T
потому что Java реализует дженерики с Стирания Типа. Почти вся общая информация стирается во время компиляции.В принципе, вы не можете этого сделать:
T member = new T();
вот хороший учебник о дженериков.
вы, кажется, не понимаете, как работают дженерики. Вы можете посмотреть на http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html В основном то, что вы может do-это что-то вроде
public class Abc<T> { T someGenericThing; public Abc(){} public T getSomeGenericThing() { return someGenericThing; } public static void main(String[] args) { // create an instance of "Abc of String" Abc<String> stringAbc = new Abc<String>(); String test = stringAbc.getSomeGenericThing(); } }
я реализовывал то же самое, используя следующий подход.
public class Abc<T> { T myvar; public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException { return clazz.newInstance(); } }
Я пытался найти лучший способ добиться того же.
разве это возможно?
Тип Стирания Обходной Путь
вдохновленный @Мартина, я написал вспомогательный класс, который позволяет мне устранить проблему стирания типа. Используя этот класс (и немного уродливый трюк) я могу создать новый экземпляр из шаблона типа:
public abstract class C_TestClass<T > { T createTemplateInstance() { return C_GenericsHelper.createTemplateInstance( this, 0 ); } public static void main( String[] args ) { ArrayList<String > list = new C_TestClass<ArrayList<String > >(){}.createTemplateInstance(); } }
уродливый трюк здесь, чтобы сделать класс
abstract
, Так что пользователь класса вынужден подтип его. Здесь я подкласс его путем добавления{}
после вызова конструктора. Это определяет новый анонимный класс и создает его экземпляр.когда-то универсальный класс с подтипами конкретные типы шаблонов, я в состоянии получить типы шаблонов.
public class C_GenericsHelper { /** * @param object instance of a class that is a subclass of a generic class * @param index index of the generic type that should be instantiated * @return new instance of T (created by calling the default constructor) * @throws RuntimeException if T has no accessible default constructor */ @SuppressWarnings( "unchecked" ) public static <T> T createTemplateInstance( Object object, int index ) { ParameterizedType superClass = (ParameterizedType )object.getClass().getGenericSuperclass(); Type type = superClass.getActualTypeArguments()[ index ]; Class<T > instanceType; if( type instanceof ParameterizedType ) { instanceType = (Class<T > )( (ParameterizedType )type ).getRawType(); } else { instanceType = (Class<T > )type; } try { return instanceType.newInstance(); } catch( Exception e ) { throw new RuntimeException( e ); } } }
похоже, вы пытаетесь создать класс, который служит точкой входа в программу в качестве универсального, и это не сработает... JVM не будет знать, какой тип он должен использовать, когда он создается при запуске приложения.
однако, если бы это был более общий случай, то что-то вроде было бы то, что вы ищете:
public MyGeneric<MyChoiceOfType> getMeAGenericObject(){ return new MyGeneric<MyChoiceOfType>(); }
или, возможно:
MyGeneric<String> objMyObject = new MyGeneric<String>();
есть хакерские способы обойти это, когда вы действительно должны это сделать.
вот пример метода преобразования, который я нахожу очень полезным; и обеспечивает один из способов определения конкретного класса универсального.
этот метод принимает коллекцию объектов в качестве входных данных и возвращает массив, где каждый элемент является результатом вызова метода получения полей для каждого объекта во входной коллекции. Например, скажем, у вас есть
List<People>
и вы хотитеString[]
содержащий все фамилия:.тип значения поля, возвращаемого геттером, задается универсальным
E
, и мне нужно создать массив типаE[]
для сохранения возвращаемого значения.сам метод немного уродлив, но код, который вы пишете, который использует его, может быть намного чище.
обратите внимание, что этот метод работает только тогда, когда где-то во входных аргументах есть объект, тип которого соответствует типу возврата, и вы можете детерминированно выяснить это. Если конкретные классы ваших входных параметров (или их подобъекты) ничего не могут сказать вам о дженериках, то этот метод не будет работать.
public <E> E[] array (Collection c) { if (c == null) return null; if (c.isEmpty()) return (E[]) EMPTY_OBJECT_ARRAY; final List<E> collect = (List<E>) CollectionUtils.collect(c, this); final Class<E> elementType = (Class<E>) ReflectionUtil.getterType(c.iterator().next(), field); return collect.toArray((E[]) Array.newInstance(elementType, collect.size())); }
полный код здесь: https://github.com/cobbzilla/cobbzilla-utils/blob/master/src/main/java/org/cobbzilla/util/collection/FieldTransformer.java#L28