Обобщения: как применить ограничения между ключами и значениями на карте - PullRequest
7 голосов
/ 13 января 2012

Проблема: у меня есть интерфейс Function Object, определенный в классе:

    public static interface FunctionObject<T>  {
        void process(T object);
    }

Мне нужен универсальный интерфейс, потому что я хотел бы использовать методы T в реализациях процесса.
Затем,в другом универсальном классе у меня есть карта, где у меня есть классы в качестве ключей и функциональные объекты в качестве значений:

    Map<Class<T>, FunctionObject<T>> map;

Но я также хочу, чтобы карта принимала классы подтипов и функциональные объекты супертипов типа KEY, поэтому я сделал это:

    Map<Class<? extends T>, FunctionObject<? super T>> map; //not what I need

Основная идея состоит в том, чтобы иметь возможность использовать карту следующим образом:

    //if T were Number, this should be legal
    map.put(Class<Integer>, new FunctionObject<Integer>(){...});
    map.put(Class<Float>, new FunctionObject<Number>(){...});
    map.put(Class<Double>, new FunctionObject<Object>(){...});

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

    Map<Class<E extends T>, FunctionObject<? super E>>> map;

Как я могу достичь желаемого эффекта?Является ли типобезопасный гетерогенный контейнер единственным вариантом?Как будут выглядеть универсальные типы Map, чтобы можно было заполнять их из ссылки?

Ответы [ 2 ]

4 голосов
/ 13 января 2012

Параметризованный контейнер, кажется, работает нормально:

public class MyMap<T>
{
    interface FunctionObject<X> {}

    private Map<Class<? extends T>, FunctionObject<Object>> map = new HashMap<>();

    @SuppressWarnings("unchecked")
    public <E extends T> void put(Class<E> c, FunctionObject<? super E> f)
    {
        map.put(c, (FunctionObject<Object>) f);
    }

    public <E extends T> FunctionObject<Object> get(Class<E> c)
    {
        return map.get(c);
    }

    public static void Main(String[] args)
    {
        MyMap<Number> map = new MyMap<>();

        map.put(Integer.class, new FunctionObject<Integer>() {});
        map.put(Float.class, new FunctionObject<Number>() {});
        map.put(Double.class, new FunctionObject<Object>() {});
    }
}

Отредактировано для соответствия вопросу. К сожалению, нет способа избежать опускания до объекта.

Редактировать добавлено get().

4 голосов
/ 13 января 2012

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

Следующий метод добавления позволяет избежать необходимости удваивать также тип

public class Main {
interface FunctionObject<T> { }

private final Map<Class, FunctionObject> map = new LinkedHashMap<Class, FunctionObject>();

public <T> void add(FunctionObject<T> functionObject) {
    Class<T> tClass = null;
    for (Type iType : functionObject.getClass().getGenericInterfaces()) {
        ParameterizedType pt = (ParameterizedType) iType;
        if (!pt.getRawType().equals(FunctionObject.class)) continue;
        Type t = pt.getActualTypeArguments()[0];
        tClass = (Class<T>) t;
        break;
    }
    map.put(tClass, functionObject);
}

public <T> void put(Class<T> tClass, FunctionObject<T> functionObject) {
    map.put(tClass, functionObject);
}

public <T> FunctionObject<T> get(Class<T> tClass) {
    return map.get(tClass);
}

public static void main(String... args) throws IOException {
    Main m = new Main();
    m.add(new FunctionObject<Integer>() {
    });
    FunctionObject<Integer> foi = m.get(Integer.class);
    System.out.println(foi.getClass().getGenericInterfaces()[0]);
}
}

отпечатки

Main.Main$FunctionObject<java.lang.Integer>

Вы можете использовать @SuppressWarnings("unchecked"), если хотите отключить предупреждение.

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

...