Общая карта общего ключа/значений со связанными типами
Я пытаюсь создать общий тип, который хранит карту самих версий, которые были созданы для последующего использования. Фактически, это одноэлементный шаблон, где есть один экземпляр для каждого типа. Код, который у меня есть до сих пор:
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 предупреждает о возвращении, только усиливает эту мысль.
Есть ли лучший способ справиться с этим?
Примечание: Этот вопрос кажется очень тесно связанным, но достаточно далеко, что я не могу понять, как применять информацию в нем к моей собственной проблеме.
Ответы
Ответ 1
Ваша реализация верна. Там нет "лучшего" способа сделать это (если в коде есть что-то "лучше", это еще одна проблема..)
Незначительные исправления:
-
<V extends Object>
эквивалентен V
, который является менее подробным
-
Class<? extends Object>
эквивалентен Class<?>
, который является менее подробным
- Вы можете использовать аннотацию
@SuppressWarnings("unchecked")
, чтобы сообщить своему компилятору, что бросок безопасен
Ответ 2
Я не думаю, что это можно сделать без какого-либо непроверенного броска. Вам понадобится нечто похожее на 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);
Ответ 3
RHSeeger, я получил ваш оригинальный вопрос. Я не нашел решения проблемы. То, с чем вы можете попытаться сыграть, - это класс MyMap, который делает привязку по вашему запросу. Однако с этой картой возникают две проблемы:
- Как объявлено как
MyMap<?>
, нельзя добавить что-то с заданным типом. Этот манекен, и я отсылаю вас к часто задаваемые вопросы Java Generics (см. Пример 3).
- Поскольку у карты есть связь между ключом и значением, нельзя добавить два независимых объекта любого типа (два
<?>
относятся к разным типам), потому что эти два типа могут быть не связаны.
Во время игры я видел некоторые ошибки, которые я не мог объяснить сам. Я думаю, все идет в том, что (как я упоминал ранее) мы пытаемся решить с параметризацией 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
}
}
Ответ 4
В приведенном выше примере указывается, как восстановить тип (класс) объекта, в то время как вам нужно восстановить тип (класс) параметризации. Это невозможно.