Создать экземпляр универсального типа в Java? - PullRequest
535 голосов
/ 16 сентября 2008

Можно ли создать экземпляр универсального типа в Java? Я думаю, основываясь на том, что я видел, что ответ no ( из-за стирания типа ), но мне было бы интересно, если кто-то может увидеть то, что мне не хватает:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

РЕДАКТИРОВАТЬ: Оказывается, Super Type Tokens можно использовать для решения моей проблемы, но для этого требуется много основанного на отражении кода, как указано в некоторых ответах ниже.

Я оставлю это открытым на некоторое время, чтобы посмотреть, придет ли кто-нибудь что-нибудь кардинально отличное от Артима Яна Робертсона .

Ответы [ 25 ]

6 голосов
/ 18 сентября 2008

Если вы не хотите вводить имя класса дважды во время создания экземпляра, как в:

new SomeContainer<SomeType>(SomeType.class);

Вы можете использовать фабричный метод:

<E> SomeContainer<E> createContainer(Class<E> class); 

Как в:

public class Container<E> {

    public static <E> Container<E> create(Class<E> c) {
        return new Container<E>(c);
    }

    Class<E> c;

    public Container(Class<E> c) {
        super();
        this.c = c;
    }

    public E createInstance()
            throws InstantiationException,
            IllegalAccessException {
        return c.newInstance();
    }

}
5 голосов
/ 10 февраля 2016

К сожалению, Java не позволяет вам делать то, что вы хотите. См. официальный обходной путь :

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

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

В качестве обходного пути вы можете создать объект параметра типа с помощью отражения:

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

Вы можете вызвать метод append следующим образом:

List<String> ls = new ArrayList<>();
append(ls, String.class);
4 голосов
/ 12 февраля 2014

Когда вы работаете с E во время компиляции, вы на самом деле не заботитесь о фактическом универсальном типе «E» (либо используете отражение, либо работаете с базовым классом универсального типа), поэтому пусть подкласс предоставляет экземпляр E. *

Abstract class SomeContainer<E>
{

    abstract protected  E createContents();
    public doWork(){
        E obj = createContents();
        // Do the work with E 

     }
}


**BlackContainer extends** SomeContainer<Black>{
    Black createContents() {
        return new  Black();
    }
}
3 голосов
/ 18 декабря 2008

Вы можете использовать:

Class.forName(String).getConstructor(arguments types).newInstance(arguments)

Но вам нужно указать точное имя класса, включая пакеты, например. java.io.FileInputStream. Я использовал это для создания парсера математических выражений.

2 голосов
/ 04 января 2013

Я думал, что смогу сделать это, но довольно разочарован: это не работает, но я думаю, что все же стоит поделиться

Может быть, кто-то может исправить:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeContainer<E> {
    E createContents();
}

public class Main {

    @SuppressWarnings("unchecked")
    public static <E> SomeContainer<E> createSomeContainer() {
        return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class[]{ SomeContainer.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> returnType = method.getReturnType();
                return returnType.newInstance();
            }
        });
    }

    public static void main(String[] args) {
        SomeContainer<String> container = createSomeContainer();

    [*] System.out.println("String created: [" +container.createContents()+"]");

    }
}

Производит:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

Строка 26 - это строка с [*].

Единственное жизнеспособное решение - решение от @ JustinRudd

2 голосов
/ 07 ноября 2014

Улучшение ответа @ Ноа.

Причина изменения

a] Безопаснее, если используется более 1 универсального типа, если вы изменили заказ.

b] Время от времени меняется сигнатура универсального типа класса, поэтому вы не будете удивлены необъяснимыми исключениями во время выполнения.

Надежный код

public abstract class Clazz<P extends Params, M extends Model> {

    protected M model;

    protected void createModel() {
    Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
    for (Type type : typeArguments) {
        if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) {
            try {
                model = ((Class<M>) type).newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

Или используйте один вкладыш

Однострочный код

model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();
0 голосов
/ 04 декабря 2014

Вот реализация createContents, которая использует TypeTools для разрешения необработанного класса, представленного E:

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

Этот подход работает, только если SomeContainer находится в подклассах, поэтому фактическое значение E фиксируется в определении типа:

class SomeStringContainer extends SomeContainer<String>

В противном случае значение E стирается во время выполнения и не подлежит восстановлению.

0 голосов
/ 16 сентября 2008

Если вы имеете в виду new E() тогда это невозможно. И я бы добавил, что это не всегда правильно - как узнать, есть ли у E открытый конструктор no-args? Но вы всегда можете делегировать создание другому классу, который знает, как создать экземпляр - это может быть Class<E> или ваш пользовательский код, подобный этому

interface Factory<E>{
    E create();
}    

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}
0 голосов
/ 12 июня 2014

Существуют различные библиотеки, которые могут разрешить E для вас, используя методы, аналогичные тем, которые обсуждались в статье Робертсона. Вот реализация createContents, которая использует TypeTools для разрешения необработанного класса, представленного E:

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

Предполагается, что getClass () преобразуется в подкласс SomeContainer и в противном случае завершится ошибкой, поскольку фактическое параметризованное значение E будет стерто во время выполнения, если оно не будет записано в подкласс.

0 голосов
/ 04 февраля 2014

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

final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...