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

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

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

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

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

Ответы [ 25 ]

0 голосов
/ 28 декабря 2018

Вот улучшенное решение, основанное на ParameterizedType.getActualTypeArguments, уже упомянутое @noah, @Lars Bohl и некоторыми другими.

Первое небольшое улучшение в реализации. Фабрика должна возвращать не экземпляр, а тип. Как только вы возвращаете экземпляр, используя Class.newInstance(), вы уменьшаете область использования. Потому что только конструкторы без аргументов могут быть вызваны следующим образом. Лучший способ - вернуть тип и позволить клиенту выбрать, какой конструктор он хочет вызвать:

public class TypeReference<T> {
  public Class<T> type(){
    try {
      ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
      if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
        throw new IllegalStateException("Could not define type");
      }
      if (pt.getActualTypeArguments().length != 1){
        throw new IllegalStateException("More than one type has been found");
      }
      Type type = pt.getActualTypeArguments()[0];
      String typeAsString = type.getTypeName();
      return (Class<T>) Class.forName(typeAsString);

    } catch (Exception e){
      throw new IllegalStateException("Could not identify type", e);
    }

  }
}

Вот примеры использования. @Lars Bohl показал только лучший способ получить обобщенный род посредством расширения. @noah только через создание экземпляра с {}. Вот тесты, чтобы продемонстрировать оба случая:

import java.lang.reflect.Constructor;

public class TypeReferenceTest {

  private static final String NAME = "Peter";

  private static class Person{
    final String name;

    Person(String name) {
      this.name = name;
    }
  }

  @Test
  public void erased() {
    TypeReference<Person> p = new TypeReference<>();
    Assert.assertNotNull(p);
    try {
      p.type();
      Assert.fail();
    } catch (Exception e){
      Assert.assertEquals("Could not identify type", e.getMessage());
    }
  }

  @Test
  public void reified() throws Exception {
    TypeReference<Person> p = new TypeReference<Person>(){};
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }

  static class TypeReferencePerson extends TypeReference<Person>{}

  @Test
  public void reifiedExtenension() throws Exception {
    TypeReference<Person> p = new TypeReferencePerson();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }
}

Примечание: вы можете заставить клиентов TypeReference всегда использовать {} при создании экземпляра, делая этот класс абстрактным: public abstract class TypeReference<T>. Я не сделал этого, только чтобы показать стертый тестовый пример.

0 голосов
/ 06 апреля 2012

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

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 );
   }
}
0 голосов
/ 22 марта 2011
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
0 голосов
/ 16 января 2019

Надеюсь, что еще не поздно помочь !!!

Java - это безопасность типов, только объект может создавать экземпляр.

В моем случае я не могу передать параметры в метод createContents. Мое решение заключается в использовании расширений вместо всех приведенных ниже ответов.

private static class SomeContainer<E extends Object> {
    E e;
    E createContents() throws Exception{
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    }
}

Это мой пример, в котором я не могу передать параметры.

public class SomeContainer<E extends Object> {
    E object;

    void resetObject throws Exception{
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    }
}

Использование рефлексии создает ошибку времени выполнения, если вы расширяете свой универсальный класс без типа объекта. Для расширения вашего универсального типа на объект преобразуйте эту ошибку во время компиляции.

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

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

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