Создание экземпляра дженериков в java

Duplicate: Java generics, почему это не работает

Дубликат: создание родового класса в Java

Я хотел бы создать объект типа Generics в java. Пожалуйста, предложите, как я могу достичь того же.

Примечание. Это может показаться тривиальной проблемой Generics. Но я уверен, что это не так.:)

Предположим, что у меня есть объявление класса:

public class Abc<T> {
    public T getInstanceOfT() {
       // I want to create an instance of T and return the same.
    }
}

Ответ 1

public class Abc<T> {
    public T getInstanceOfT(Class<T> aClass) {
       return aClass.newInstance();
    }
}

Вам нужно будет добавить обработку исключений.

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

Ответ 2

В отправленном вами коде невозможно создать экземпляр 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() на объект Class.

Если вы подклассом Abc<T>, вы можете даже обойти проблему стирания типа и не должны передавать любые ссылки Class<T>:

import java.lang.reflect.ParameterizedType;

public class Abc<T>
{
    T getInstanceOfT()
    {
        ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
        Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
        try
        {
            return type.newInstance();
        }
        catch (Exception e)
        {
            // Oops, no default constructor
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args)
    {
        String instance = new SubClass().getInstanceOfT();
        System.out.println(instance.getClass());
    }
}

class SubClass
    extends Abc<String>
{
}

Ответ 3

То, что вы написали, не имеет никакого смысла, генерики в Java предназначены для добавления функциональности параметрического полиморфизма к объектам.

Что это значит? Это означает, что вы хотите, чтобы некоторые переменные типа ваших классов не определились, чтобы иметь возможность использовать ваши классы со многими различными типами.

Но ваша переменная type T - это атрибут, который разрешен во время выполнения, компилятор Java скомпилирует вашу безопасность класса, не пытаясь узнать, что это за объект T, поэтому он не позволяет ему вы используете переменную типа в статическом методе. Тип связан с экземпляром времени выполнения объекта, а public void static main(..) связан с определением класса, и в этой области T ничего не значит.

Если вы хотите использовать переменную типа внутри статического метода, вы должны объявить метод как общий (это потому, что, поскольку объясненные переменные типа класса шаблона связаны с его экземпляром времени выполнения), а не класс:

class SandBox
{
  public static <T> void myMethod()
  {
     T foobar;
  }
}

это работает, но, конечно, не с методом main, так как нет способа вызвать его общим способом.

EDIT. Проблема заключается в том, что из-за стирания типа только один общий класс скомпилирован и передан в JVM. Контроллер типа проверяет, безопасен ли код, а затем, поскольку он доказал, что всякая родовая информация отбрасывается.

Чтобы создать экземпляр 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());
    }
}

Конечно, это означает, что тип, который вы хотите создать:

  • не является примитивным типом
  • у него есть конструктор по умолчанию

Чтобы работать с разными конструкторами, вы должны углубиться в размышления.

Ответ 4

Вам нужно получить информацию типа статически. Попробуйте следующее:

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>.

Ответ 5

Единственный способ заставить его работать - использовать Reified Generics. И это не поддерживается в Java (но это было запланировано для Java 7, но было отложено). В С#, например, поддерживается, предполагая, что T имеет конструктор по умолчанию. Вы даже можете получить тип времени выполнения typeof(T) и получить конструкторы Type.GetConstructor(). Я не делаю С#, поэтому синтаксис может быть недействительным, но он примерно выглядит следующим образом:

public class Foo<T> where T:new() {
    public void foo() {
        T t = new T();
    }
}

Лучшим "обходным путем" для этого в Java является передача аргумента Class<T> как метода, как уже отмечалось несколько ответов.

Ответ 6

Прежде всего, вы не можете получить доступ к параметру типа T в статическом основном методе, только для нестатических членов класса (в данном случае).

Во-вторых, вы не можете создавать экземпляры T, потому что Java реализует дженерики с типом Erasure. Почти вся общая информация удаляется во время компиляции.

В принципе, вы не можете этого сделать:

T member = new T();

Вот хороший учебник по generics.

Ответ 7

Кажется, вы не понимаете, как работают Generics. Вы можете посмотреть http://java.sun.com/j2se/1.5.0/docs/guide/language/generics.html В основном, что вы можете сделать, это что-то вроде

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();
  }

} 

Ответ 8

Я реализовал то же самое, используя следующий подход.

public class Abc<T>
{
   T myvar;

    public T getInstance(Class<T> clazz) throws InstantiationException, IllegalAccessException
    {
        return clazz.newInstance();
    }
}

Я пытался найти лучший способ добиться того же.

Разве это невозможно?

Ответ 9

Обходное решение типа Erasure

Вдохновленный @martin answer, я написал класс-помощник, который позволяет мне обходить проблему стирания типа. Используя этот класс (и немного уродливый трюк), я могу создать новый экземпляр из типа шаблона:

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 );
        }
    }
}

Ответ 10

Похоже, вы пытаетесь создать класс, который служит точкой входа в ваше приложение как общий, и это не сработает... JVM не будет знать, какой тип он должен использовать, когда он запускается при запуске приложения.

Однако, если это был более общий случай, то что-то вроде того, что вы ищете:

public MyGeneric<MyChoiceOfType> getMeAGenericObject(){
   return new MyGeneric<MyChoiceOfType>();
}

или, возможно:

MyGeneric<String> objMyObject = new MyGeneric<String>();

Ответ 11

Есть вредные способы вокруг этого, когда вам действительно нужно это делать.

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

Этот метод принимает коллекцию объектов в качестве входных данных и возвращает массив, в котором каждый элемент является результатом вызова поля getter для каждого объекта во входной коллекции. Например, скажем, что у вас есть 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

Ответ 12

Abc<String> abcInstance = new Abc<String> ();

.. например