Как передать параметризованный класс в качестве аргумента

Моя цель - создать класс, который может выводить объект определенного класса.

public class GetMe<T> {
    public T get() {
        Object obj = generateObject();
        return (T) obj;
    }
}

Теперь я знаю, что это невозможно из-за стирания. Итак, мы можем передать экземпляр класса и использовать его для создания.

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

    public T get() {
        Object obj = generateObject();
        return clazz.cast(obj);
    }
}

Это отлично работает! Пока класс не параметризуется. Если да, то у меня проблема.

Мне не разрешено использовать List<String>.class. Если я передаю параметр ParameterizedType (который сам по себе сложно сгенерировать), не существует метода cast.

Есть ли выход из этой трясины?

Ответы

Ответ 2

Проблема с List<String> заключается в том, что из-за стирания это было бы во время выполнения неотличимым от любого другого List<?>. Самый простой способ - создать новый класс или интерфейс, который имеет общую "фиксированную" часть, например

public interface StringList extends List<String> {
    /* nothing to see here */
}

Таким образом, у вас есть токен типа (объект StringList.class), который вы можете передавать во время выполнения, и указывает именно то, что вы хотите, но без необходимости создания дженериков во время выполнения.

Ответ 3

Вот только небольшая идея. Я не уверен, будет ли он соответствовать вашему контексту, но тем не менее:

public class GetMe<T>
{
    public List<T> getList() {
        @SuppressWarnings("unchecked")
        List<T> result = (List<T>) new LinkedList(); 
        return result;
    }
}

Ура!

Ответ 4

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

Вы можете использовать Type вместо класса. Тип может представлять все общие типы, хотя с ним не приятно работать.

abstract public class GetMe<T> 
{
    Type type;
    public GetMe<T>(Type type) 
    {
        this.type = type;
    }
}

Другая проблема заключается в том, как создать общий тип типа List<String>. "Символ супер-типа" выглядит аккуратным в синтаксисе, на самом деле он в основном

static class XX extends TypeReference<List<String>>{}
....
Type typeListString = Util.extract(XX.class);

Я бы предпочел этот вариант

List<String> f;

Type typeListString = getDeclaredField("f").getGenericType();

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

Ответ 5

Я думаю, что путаница возникает из-за того, что вы пытаетесь создать объект из List<>, который на первый взгляд является интерфейсом, а не объектом.
Таким образом, независимо от того, что вы попробуете, вы просто не можете создать экземпляр List<>, (интерфейсы не являются фактическими классами и не имеют конструкторов)

Попробуйте использовать ограничение, чтобы не вставлять интерфейсы в объявление:

public class GetMe<T extends Object> 

Это гарантирует, что T является фактическим классом, а не интерфейсом.