Универсальная карта универсального ключа / значений со связанными типами

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

public class FieldBinder<T> {
    static final Map<Class<? extends Object>,FieldBinder<? extends Object>> instanceMap = 
        new HashMap<Class<? extends Object>,FieldBinder<? extends Object>>();

    private FieldBinder() {}

    synchronized public static <V extends Object> FieldBinder<V> getInstance(Class<V> klass) {
        if(!instanceMap.containsKey(klass)) {
            instanceMap.put(klass, new FieldBinder<V>());
        }
        return (FieldBinder<V>)instanceMap.get(klass);
    }
}

Тем не менее, я все еще не уверен, что я "делаю это правильно". Такое чувство, что я должен быть в состоянии указать, что коллекция (Class -> FieldBinder). Тот факт, что IDE предупреждает о возврате, только усиливает эту мысль.

Есть ли лучший способ справиться с этим?

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

4 ответа

Решение

Ваша реализация верна. Нет "лучшего" способа сделать это (если есть такая вещь, "лучше" в коде, что является другой проблемой..)

Незначительные исправления:

  • <V extends Object> эквивалентно V который менее многословен
  • Class<? extends Object> эквивалентно Class<?> который менее многословен
  • Вы можете использовать @SuppressWarnings("unchecked") аннотация, чтобы сказать вашему компилятору, что приведение безопасно

RHSeeger, я получил ваш оригинальный вопрос. Я не нашел решения этой проблемы. Вы можете попробовать поиграть с классом MyMap, который создает привязку по вашему запросу. Однако с этой картой возникают две проблемы:

  1. Как заявлено как MyMap<?> нельзя добавить что-либо с данным типом к этому. Это глупо, и я отсылаю вас к часто задаваемым вопросам по Java Generics (см. Пример 3) для более подробной информации.
  2. Поскольку карта имеет связь между ключом и значением, нельзя добавить два независимых объекта любого типа (два <?> относятся к разным типам), потому что эти два типа могут быть не связаны.

Во время игры я видел некоторые ошибки, которые я не мог объяснить сам. Я думаю, что все сводится к тому (как я упоминал ранее), что мы пытаемся иметь дело с параметризацией 2-го уровня.

    class FieldBinder<T> {
        static class MyMap<M> extends HashMap<Class<M>, FieldBinder<M>> {
        }
        static final MyMap<?> instanceMap1 = new MyMap<Object>();
        static final Map<Class<?>, FieldBinder<?>> instanceMap2 = new HashMap<Class<?>, FieldBinder<?>>();
        public static <V> void test() {
            Class<V> c1 = null;
            FieldBinder<V> f1 = null;
            Class<?> c2 = null;
            FieldBinder<?> f2 = null;
            instanceMap1.put(c1, f1); // error (see 1)
            instanceMap1.put(c2, f2); // error (see 2)
            instanceMap2.put(c1, f1); // ok
            instanceMap2.put(c2, f2); // ok
            instanceMap2.put(c1, f2); // wish to be an error, but ok
            instanceMap2.put(c2, f1); // wish to be an error, but ok
        }
    }

Я не думаю, что это может быть сделано без неконтролируемого каста где-нибудь. Вам нужно что-то похожее на экзистенциальные типы Haskell, которых нет в Java.

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

synchronized public static <V> FieldBinder<V>
getInstance(Class<V> klass, Class<FieldBinder<V>> binderKlass) {
    if(!instanceMap.containsKey(klass)) {
        instanceMap.put(klass, new FieldBinder<V>());
    }
    return binderKlass.cast(instanceMap.get(klass));
}

Теперь, если клиент проходит Class<FieldBinder<V>> к getInstance() метод, который вы можете избежать непроверенного getInstance(),

К сожалению создание Class<FieldBinder<V>> само по себе требует неконтролируемого броска.

Class<FieldBinder<Integer>> binderKlass =
    (Class<FieldBinder<Integer>>) (Class<?>) FieldBinder.class;
BinderAssociator.getInstance(Integer.class, binderKlass);

В приведенном вами примере рассказывается, как восстановить тип (класс) объекта, в то время как вам необходимо восстановить тип (класс) параметризации. Это невозможно.

Другие вопросы по тегам