Java - расширение HashMap - поведение объекта и дженериков
Я пишу простой кэш HashMap
, который работает следующим образом:
- Если запрошенный
key
в кеше, верните его value
.
- Если запрошенный
key
не, запустите метод, который создает value
на основе key
, сохраните оба, верните value
.
Код:
import java.util.HashMap;
abstract class Cache<K, V> extends HashMap<K, V> {
@Override
public V get(Object key) {
if (containsKey(key)) {
return super.get(key);
} else {
V val = getData(key);
put((K)key, val); // this is the line I'm discussing below
return val;
}
}
public abstract V getData(Object key);
}
Это довольно просто и хорошо работает. Тем не менее, я ненавижу решение Sun для get()
взять Object
в качестве своего аргумента, а не K
. Я достаточно читал об этом, чтобы знать, что у него есть некоторые соображения позади этого (с которым я не согласен, но это другая история).
Моя проблема заключается в прокомментированной строке, потому что кажется, что актер должен быть снят. Из-за стирания стилей я не могу проверить, имеет ли тип key
тип K
(который необходим для правильной put()
функциональности), поэтому метод, следовательно, подвержен ошибкам.
Одним из решений было бы перейти от отношения "есть" к "имеет" HashMap
, которое намного лучше и чище, но тогда Cache
не может реализовать Map
, что было бы неплохо по нескольким причинам. Код:
import java.util.HashMap;
import java.util.Map;
abstract class Cache<K, V> {
private final Map<K, V> map = new HashMap<K, V>();
public V get(K key) {
if (map.containsKey(key)) {
return map.get(key);
} else {
V val = getData(key);
map.put(key, val);
return val;
}
}
public abstract V getData(K key);
}
Может ли кто-нибудь придумать любое другое (даже хакерское) решение, чтобы я мог поддерживать Cache
как Map
и по-прежнему быть безопасным в терминах get(Object key)
и put(K key, V val)
?
Единственное, о чем я могу думать, это сделать другой метод с именем ie getValue(Key k)
, который делегировал бы get(Object key)
, но тогда я не могу заставить кого-либо использовать новый метод вместо обычного.
Ответы
Ответ 1
Неа. Вы нашли правильное решение при переключении на отношения "has-a". (Честно говоря, если метод get
вычислить новое значение, если он еще не существует, является неожиданным, нарушает договор Map
и может привести к чрезвычайно странному поведению для ряда других методов. Это была большая часть почему Guava отошел от MapMaker
, который предлагал почти это точное поведение, потому что это было просто с проблемами.)
Тем не менее, что, например, Guava Cache
делает ли он представление Map<K, V> asMap()
, которое вы можете сделать. Это дает вам большинство преимуществ Map
без ущерба для безопасности типов.
Ответ 2
Определенно отношение has-a является правильной реализацией. бизнес-логика того, как генерируется значение, должна быть удалена из класса кэша.