типы - тип long java




Создать экземпляр родового типа в Java? (16)

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

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

EDIT: Оказывается, супертекстовые маркеры могут использоваться для решения моей проблемы, но для этого требуется много кода на основе отражения, как указывали некоторые из приведенных ниже ответов.

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


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

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

Итак, когда вы подклассом Foo, вы получаете экземпляр Bar, например,

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

Но это большая работа и работает только для подклассов. Может быть удобно.


Java досадно не позволяет делать то, что вы хотите. См. Http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createObjects

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

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

Вам понадобится какая-то абстрактная фабрика того или иного типа, чтобы передать доллар:

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

class SomeContainer<E> {
    private final Factory<E> factory;
    SomeContainer(Factory<E> factory) {
        this.factory = factory;
    }
    E createContents() {
        return factory.create();
    }
}

Вмешательство в ответ @ Ноя.

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

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

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

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

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

class SomeStringContainer extends SomeContainer<String>

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


Вы можете добиться этого с помощью следующего фрагмента:

import java.lang.reflect.ParameterizedType;

public class SomeContainer<E> {
   E createContents() throws InstantiationException, IllegalAccessException {
      ParameterizedType genericSuperclass = (ParameterizedType)
         getClass().getGenericSuperclass();
      @SuppressWarnings("unchecked")
      Class<E> clazz = (Class<E>)
         genericSuperclass.getActualTypeArguments()[0];
      return clazz.newInstance();
   }
   public static void main( String[] args ) throws Throwable {
      SomeContainer< Long > scl = new SomeContainer<>();
      Long l = scl.createContents();
      System.out.println( l );
   }
}

Вы можете сделать это сейчас, и это не требует кучу кода отражения.

import com.google.common.reflect.TypeToken;

public class Q26289147
{
    public static void main(final String[] args) throws IllegalAccessException, InstantiationException
    {
        final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
        final String string = (String) smpc.type.getRawType().newInstance();
        System.out.format("string = \"%s\"",string);
    }

    static abstract class StrawManParameterizedClass<T>
    {
        final TypeToken<T> type = new TypeToken<T>(getClass()) {};
    }
}

Конечно, если вам нужно вызвать конструктор, который потребует некоторого отражения, но это очень хорошо документировано, этот трюк не таков!

Вот JavaDoc для TypeToken .


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

public final class Foo<T> {

    private Class<T> typeArgumentClass;

    public Foo(Class<T> typeArgumentClass) {

        this.typeArgumentClass = typeArgumentClass;
    }

    public void doSomethingThatRequiresNewT() throws Exception {

        T myNewT = typeArgumentClass.newInstance();
        ...
    }
}

Использование:

Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);

Плюсы:

  • Гораздо проще (и менее проблематично), чем подход Robertson Super Type Token (STT).
  • Гораздо эффективнее, чем подход STT (который будет есть ваш мобильный телефон на завтрак).

Минусы:

  • Не удается передать класс конструктору по умолчанию (поэтому Foo является окончательным). Если вам действительно нужен конструктор по умолчанию, вы всегда можете добавить метод setter, но затем вы должны помнить, чтобы позвонить ей позже.
  • Возражение Роберсона ... Больше баров, чем черная овца (хотя определение класса аргументов типа еще раз не приведет вас точно). И, вопреки утверждениям Роберсона, это никак не нарушает принцип DRY, потому что компилятор будет гарантировать правильность типа.
  • Не полностью Foo<L> доказательство. Для начала ... newInstance() будет вызывать wobbler, если класс аргумента type не имеет конструктора по умолчанию. Однако это относится ко всем известным решениям.
  • Отсутствует полная инкапсуляция подхода STT. Неважно, хотя (учитывая возмутительные эксплуатационные издержки STT).

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

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

}

Из учебника 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);

Когда вы работаете с 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();
    }
}

Подумайте о более функциональном подходе: вместо создания некоторого E из ничего (что явно является запахом кода) передайте функцию, которая знает, как ее создать, т. Е.

E createContents(Callable<E> makeone) {
     return makeone.call(); // most simple case clearly not that useful
}

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

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

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


Ты прав. Вы не можете выполнить new E() . Но вы можете изменить его на

private static class SomeContainer<E> {
    E createContents(Class<E> clazz) {
        return clazz.newInstance();
    }
}

Это боль. Но это работает. Обертка его в заводской схеме делает ее более переносимой.


package org.foo.com;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{

    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>
    {
    }

    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }

    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException
    {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }

}

return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();




generics