Создание экземпляра универсального класса в Java

Я знаю, что дженерики Java несколько уступают .Net.

У меня есть общий класс Foo<T>, и мне действительно нужно создать экземпляр T в Foo с помощью конструктора без параметров. Как можно обойти ограничение Java?

Ответ 1

Один из вариантов состоит в том, чтобы передать Bar.class (или любой другой тип, который вас интересует, любым способом указания соответствующей ссылки Class<T>) и сохранить это значение как поле:

public class Test
{   
    public static void main(String [] args)
        throws Exception // Just for simplicity!
    {
        Generic<Bar> x = new Generic<Bar>(Bar.class);
        Bar y = x.buildOne();
    }
}

public class Generic<T>
{
    private Class<T> clazz;

    public Generic(Class<T> clazz)
    {
        this.clazz = clazz;
    }

    public T buildOne() throws InstantiationException,
        IllegalAccessException
    {
        return clazz.newInstance();
    }
}

public class Bar
{
    public Bar()
    {
        System.out.println("Constructing");
    }
}

Другой вариант - иметь интерфейс "factory", и вы передаете factory конструктору универсального класса. Это более гибко, и вам не нужно беспокоиться об исключениях отражения.

Ответ 2

И это реализация Factory, поскольку предложил Jon Skeet:

interface Factory<T> {
    T factory();
}

class Araba {
    //static inner class for Factory<T> implementation
    public static class ArabaFactory implements Factory<Araba> {
        public Araba factory() {
            return new Araba();
        }
    }
    public String toString() { return "Abubeee"; }
}

class Generic<T> {
    private T var;

    Generic(Factory<T> fact) {
        System.out.println("Constructor with Factory<T> parameter");
        var = fact.factory();
    }
    Generic(T var) {
        System.out.println("Constructor with T parameter");
        this.var = var;
    }
    T get() { return var; }
}

public class Main {
    public static void main(String[] string) {
        Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
        System.out.print(gen.get());
    }
}

Вывод:

Constructor with Factory<T> parameter
Abubeee

Ответ 3

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

public class Test {   
    public static void main(String [] args) throws Exception {
        Generic g = new Generic();
        g.initParameter();
    }
}

import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
    protected T parameter;

    @SuppressWarnings("unchecked")
    void initParameter() throws Exception, ClassNotFoundException, 
        InstantiationException {
        // Get the class name of this instance type.
        ParameterizedType pt
            = (ParameterizedType) getClass().getGenericSuperclass();
        // You may need this split or not, use logging to check
        String parameterClassName
            = pt.getActualTypeArguments()[0].toString().split("\\s")[1];
        // Instantiate the Parameter and initialize it.
        parameter = (T) Class.forName(parameterClassName).newInstance();
    }
}

public class Generic extends GenericAbstract<Foo> {
}

public class Foo {
    public Foo() {
        System.out.println("Foo constructor...");
    }
}

Ответ 4

Мне действительно нужно создать экземпляр T в Foo, используя параметр-less  Конструктор

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

Как можно обойти ограничение Java?

Один из способов (могут быть другие) - передать объект, который вы передадите экземпляру T в конструктор Foo<T>. Или у вас может быть метод setBar(T theInstanceofT);, чтобы получить ваш T вместо создания экземпляра в классе, который он сам.

Ответ 5

Общие понятия в Java, как правило, более мощные, чем в С#.

Если вы хотите построить объект, но без аппаратного конструктора/статического метода, используйте абстрактный factory. Вы должны иметь возможность находить подробную информацию и учебные пособия по шаблону Abstract Factory в любой базовой книге шаблонов проектирования, введении в ООП или во всех средах. Это не стоит дублировать код здесь, кроме упоминания о том, что синтаксис закрытия Java отстой.

IIRC, С# имеет специальный случай для указания, что общий тип имеет конструктор no-args. Эта нерегулярность, по определению, предполагает, что клиентский код хочет использовать эту конкретную форму построения и поощряет изменчивость.

Использование отражения для этого просто неверно. Генераторы в Java - это функция статического ввода времени компиляции. Попытки использовать их во время выполнения - это четкое указание на то, что что-то происходит не так. Отражение вызывает подробный код, ошибки выполнения, необработанные зависимости и уязвимости безопасности. (Class.forName особенно злой.)

Ответ 6

От fooobar.com/questions/15894/.... Для класса T вам нужен конструктор по умолчанию.


import java.lang.reflect.ParameterizedType;

class Foo {

  public bar() {
    ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
    Class type = (Class) superClass.getActualTypeArguments()[0];
    try {
      T t = type.newInstance();
      //Do whatever with t
    } catch (Exception e) {
      // Oops, no default constructor
      throw new RuntimeException(e);
    } 
  }
}

Ответ 7

Я мог бы сделать это в JUnit Test Setup.

Я хотел протестировать фасад Hibernate, поэтому я искал общий способ сделать это. Обратите внимание, что фасад также реализует общий интерфейс. Здесь T - класс базы данных, а U - первичный ключ. Ifacade<T,U> - это фасад для доступа к объекту базы данных T с первичным ключом U.

public abstract class GenericJPAController<T, U, C extends IFacade<T,U>>

{
    protected static EntityManagerFactory emf;

    /* The properties definition is straightforward*/
    protected T testObject;
    protected C facadeManager;

    @BeforeClass
    public static void setUpClass() {


        try {
            emf = Persistence.createEntityManagerFactory("my entity manager factory");

        } catch (Throwable ex) {
            System.err.println("Failed to create sessionFactory object." + ex);
            throw new ExceptionInInitializerError(ex);
        }

    }

    @AfterClass
    public static void tearDownClass() {
    }

    @Before
    public void setUp() {
    /* Get the class name*/
        String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2].getTypeName();

        /* Create the instance */
        try {
            facadeManager = (C) Class.forName(className).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
            Logger.getLogger(GenericJPAController.class.getName()).log(Level.SEVERE, null, ex);
        }
        createTestObject();
    }

    @After
    public void tearDown() {
    }

    /**
     * Test of testFindTEntities_0args method, of class
     * GenericJPAController<T, U, C extends IFacade<T,U>>.
     * @throws java.lang.ClassNotFoundException
     * @throws java.lang.NoSuchMethodException
     * @throws java.lang.InstantiationException
     * @throws java.lang.IllegalAccessException
     */
    @Test
    public void  testFindTEntities_0args() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {

        /* Example of instance usage. Even intellisense (NetBeans) works here!*/
        try {
            List<T> lista = (List<T>) facadeManager.findAllEntities();
            lista.stream().forEach((ct) -> {
                System.out.println("Find all: " + stringReport());
            });
        } catch (Throwable ex) {
            System.err.println("Failed to access object." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }


    /**
     *
     * @return
     */
    public abstract String stringReport();

    protected abstract T createTestObject();
    protected abstract T editTestObject();
    protected abstract U getTextObjectIndex();
}

Ответ 8

Для Java 8....

В fooobar.com/questions/15408/... есть хорошее решение.

В нем используется функциональный интерфейс Java 8 Supplier

Ответ 9

Быстрое решение, которое сработало для меня. Я вижу, что для этого уже есть ответ, и это может быть даже не лучшим способом. Кроме того, для моего решения вам понадобится Gson.

Однако я столкнулся с ситуацией, когда мне нужно было создать экземпляр универсального класса типа java.lang.reflect.Type.

Следующий код создаст экземпляр класса с нулевыми переменными экземпляра.

T object = new Gson().fromJson("{}", myKnownType);

Где myKnownType известно заранее и получается через TypeToken.getType().

Теперь вы можете установить соответствующие свойства для этого объекта. Опять же, это не лучший способ сделать это, но он работает как быстрое решение, если это вам нужно.