Как создать экземпляр экземпляра универсального параметра метода в Java? - PullRequest
2 голосов
/ 13 марта 2012

Рассмотрим следующий код:

// ...
public class BaseClass
{
    public BaseClass (int theParam)
    {
        // ...whatever...
    }
}
public class DerivedType
{
    // ...Content does not matter...    
}


// ...elsewhere:

public <ElemType extends BaseClass> boolean doIt (ArrayList<ElemType> target)
{
    ElemType newElem=new ElemType (5) ; // "Cannot instantiate this type"

    // ...other code does not matter...

    return true ;
}

// ..

Как создать экземпляр типа ElemType в doIt?

Показанная конструкция выдает указанную ошибку.

ElemType.newInstance не существует, что меня удивляет.

Я прочитал практически все часто задаваемые вопросы, ответы и материалы Google, но не могу найти ничего полезного.

EDIT: Да, я знаю, что рефлексия имеет свои недостатки и не является окончательным решением по многим причинам. Вопрос не в том, «должен ли я это сделать», а в том, «как бы я это сделал».

Ответы [ 4 ]

3 голосов
/ 13 марта 2012

Как уже упоминалось, стирание типов общих типов не позволяет этого. Но вы можете достичь того, что вы хотите, как это:

public class BaseClass {

  public BaseClass(int theParam) {
    // ...whatever...
  }
  public BaseClass() {  
  }          
}

public class DerivedType extends BaseClass {
}

А теперь метод doIt () получает аргумент класса для справки:

public <D extends BaseClass> boolean doIt (ArrayList<D> target, Class<D> c)
{
    try {
        D newElem = c.getDeclaredConstructor(int.class).newInstance(5);
    } catch (Exception e) {}

    // ...other code does not matter...

    return true ;
}

И вы должны назвать это так:

    ArrayList<DerivedType> testList = new ArrayList<DerivedType>();
    testList.add(new DerivedType());
    testList.add(new DerivedType());
    doIt(testList, DerivedType.class);

Надеюсь, это поможет:)

Обратите внимание, что кто-то может захотеть быть хакером, избавиться от параметра класса и попробовать это:

 public static <D extends BaseClass> boolean doIt (ArrayList<D> target)
 {
    try {
        D newElem1 =  ((Class<D>) ((ParameterizedType) target.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).getDeclaredConstructor(int.class).newInstance(5);

    } catch (Exception e) { e.printStackTrace();}

    return true ;
    }
}

На самом деле я так и думал перед вторым редактированием. исключение, как вы упомянули (я не видел его из-за пропущенного оператора catch). Короче говоря, система времени исполнения Java не хранит параметризованные типы (в пользу обратной совместимости; поэтому это может измениться в будущем).

Итак, похоже, что это невозможно без «прикосновения» к какому-либо классу.

Однако, кроме упомянутых методов, я могу вспомнить еще две вещи . Во-первых, если и BaseClass, и класс DerivedType 'D' реализуют метод clone (), вы можете получить клон объекта из массива и затем использовать его:

         D o = target.get(0);

         D oNew = (D)((BaseClass)o).clone();
         target.add(oNew);

Полиморфизм позаботится обо всем остальном:)

Второй вариант не является реальным «решением», но его можно использовать, если вам нужен только новый экземпляр массива объектов, параметризованных по типу. Стирание типа происходит только для параметризованных типов, но не для базовых массивов (массивы reified в JVM). Так что, если у нас есть свобода изменять сигнатуру метода и нормально работать с массивами, будет работать следующее:

    public <D extends BaseClass> boolean doIt(D[] target) {
    try {
        D newD = (D) (target.getClass().getComponentType().getConstructor(int.class).newInstance(8));
        target[0] = newD;

        // The following is optional, if we want to work with Collections internally
        List<D> l = new ArrayList<D>(Arrays.asList(target));
        l.add(newD);  


    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}

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

3 голосов
/ 13 марта 2012

Обратите внимание, что generic-информация стирается компилятором во время компиляции и заменяется объектом. Внутренние дженерики просто приводятся с и на java.lang.Object.

По этой же причине сложно получить общую информацию во время выполнения, даже если это возможно.

Смотрите здесь: Google .

На личную записку: Если вам нужно сделать что-то подобное, это обычно плохой дизайн. Я был в этой ситуации пару раз, но я находил лучшее решение каждый раз :). Так что просто подумайте, действительно ли вам нужен такой грязный хак в вашем коде.

Edit: Относительно раздела комментариев требуется более подробное объяснение.

Отражение в целом следует использовать с осторожностью, так как с точки зрения разработки программного обеспечения это считается плохим дизайном. Зачем? Это может привести к некоторым трудным для поиска ошибкам, так как рефлексия изменяет естественный поток вашего приложения и использует информацию, которая не всегда видна вам во время разработки. Это кричит о неожиданном поведении.

И хотя у меня нет формальных доказательств этого, но я утверждаю, что каждый раз, когда вам нужно размышление, у вашей проблемы есть другое решение (если возможна генеративная разработка программного обеспечения;)).

Таким образом, в конце концов, в 99% всех случаев рефлексия - не более чем грязный взлом ленивого программиста. Это может быть связано с тем, что 100% всех программистов ленивы, но в любом случае.

Редактировать 2:

Так как вы все равно хотите код:

abstract class Foo<T> {
    private Class<T> tClass;

    T field;

    public void bar(Class<T> clazz) {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
                ParameterizedType paramType = (ParameterizedType)type;
                tClass = (Class<T>) paramType.getActualTypeArguments()[0];

                field = tClass.newInstance();
        }
    }
}

(взято из: Здесь )

2 голосов
/ 13 марта 2012

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

Чтобы разрешить создание ElemType, я бы поставил какой-то фабричный объект. Вы можете использовать класс отражения Java, но, вероятно, проще предоставить собственный базовый класс или интерфейс фабрики.

0 голосов
/ 13 марта 2012

Представьте, что у вас есть это:

public class DerivedType extends BaseClass
{
    // No more one argument constructor
    public DerivedType() {
        super(0);
    }
}

Тогда звонок

DerivedType d = new DerivedType(5);

недействительно ...

...