Можете ли вы создать экземпляр типового типа в конструкторе класса общего типа? [Дубликат]

9 ответов

Один из вариантов - передать 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");
    }
}

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

156
ответ дан Jon Skeet 17 August 2018 в 10:27
поделиться
  • 1
    Почему бы не передать new Foo() в качестве аргумента? – fastcodejava 12 March 2010 в 13:04
  • 2
    @fastcodejava, потому что вам нужно передать тип класса. Foo и Class - разные типы. – Özgür 12 March 2010 в 15:54

Для Java 8 ....

В https://stackoverflow.com/a/36315051/2648077 есть хорошее решение.

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

0
ответ дан Alireza Fattahi 17 August 2018 в 10:27
поделиться

Из https://stackoverflow.com/a/2434094/848072 . Для класса 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);
    } 
  }
}

45
ответ дан Community 17 August 2018 в 10:27
поделиться
  • 1
    Это все здорово и все ... это, безусловно, работает. Но ... если все, что нам нужно, это новый экземпляр Bar (так как <T> есть <Bar>), то рефлексивно создание экземпляра Bar с Class<Bar> token = Bar.class; и Bar newBar = token.newInstance(); представляется мне гораздо менее подробным. – scottb 15 August 2013 в 21:25
  • 2
    @scottb: У вас может не быть простого класса, который может быть создан с помощью конструктора no-args. В этом случае завод будет более надежным решением, чем создание экземпляров отражающего класса. – codethulhu 15 August 2013 в 21:45
  • 3
    @codethulhu: Предоставлено. Однако одним из условий исходного вопроса было его намерение использовать «конструктор без параметров». – scottb 15 August 2013 в 22:09

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

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

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

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

4
ответ дан dfa 17 August 2018 в 10:27
поделиться

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

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's 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...");
    }
}
7
ответ дан Glenn 17 August 2018 в 10:27
поделиться
  • 1
    хорошо, я вижу. вы можете даже записать его просто как анонимный класс: & quot; GenericAbstract & lt; Foo & gt; g = new GenericAbstract & lt; Foo & gt; () {}; g.initParameter (); & Quot; и "& lt; T extends Foo & gt;" не требуется, просто «& lt; T & gt;» & lt; Сделаю. но мне все это кажется бесполезным, просто запутанным способом передать Foo.class объекту, объявив еще один класс – newacct 27 July 2009 в 03:19
  • 2
    @newacct на самом деле я думаю, что это может помочь мне создать BuffedArrayList, который, наконец, поможет мне избежать этого неприятного toArray (новое что-то [0]), поэтому я могу без проблем T toArray()! – Aquarius Power 17 November 2016 в 20:10
  • 3
    @Glenn это работает и для ArrayList, но мне нужно создать такой экземпляр: new ArrayList<Foo>(){} не знаю, почему ... – Aquarius Power 18 November 2016 в 00:12

Я мог бы сделать это в 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();
}
1
ответ дан Krauss 17 August 2018 в 10:27
поделиться

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

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

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

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

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

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

0
ответ дан Markymark 17 August 2018 в 10:27
поделиться

Дженерики в Java, как правило, более мощные, чем в C #.

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

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

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

3
ответ дан Tom Hawtin - tackline 17 August 2018 в 10:27
поделиться
  • 1
    «Дженерики в Java, как правило, более мощные, чем в C #». ВТ. Они даже не охарактеризованы. – wchargin 6 June 2013 в 22:42
  • 2
    @WChargin: Да, поэтому гарантия Java generics на безопасность типов требует, чтобы ваш код компилировался без ошибок или каких-либо предупреждений. Если ваш код не генерирует никаких предупреждений, вам гарантируется, что ваш код безопасен по типу без каких-либо накладных расходов на проверку типа времени выполнения. Является ли Java-дженериком «лучше» или нет, зависит от того, являетесь ли вы наполовину полным или стеклым наполовину пустым парнем. – scottb 15 August 2013 в 21:31
  • 3
    Дженерики на Java явно уступают дженерикам в C #. – Chaos 10 April 2014 в 03:38

И это реализация 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
45
ответ дан Community 17 August 2018 в 10:27
поделиться
  • 1
    Это все здорово и все ... это, безусловно, работает. Но ... если все, что нам нужно, это новый экземпляр Bar (так как <T> есть <Bar>), то рефлексивно создание экземпляра Bar с Class<Bar> token = Bar.class; и Bar newBar = token.newInstance(); представляется мне гораздо менее подробным. – scottb 15 August 2013 в 21:25
  • 2
    @scottb: У вас может не быть простого класса, который может быть создан с помощью конструктора no-args. В этом случае завод будет более надежным решением, чем создание экземпляров отражающего класса. – codethulhu 15 August 2013 в 21:45
  • 3
    @codethulhu: Предоставлено. Однако одним из условий исходного вопроса было его намерение использовать «конструктор без параметров». – scottb 15 August 2013 в 22:09
Другие вопросы по тегам:

Похожие вопросы: