stworzyliśmy klasę abstrakcyjną, które używamy do czynienia z Redis (set/get wartości), który wygląda jak następuje:rodzajowych Java i Serializable
public abstract class AbstractCachedSupport<T extends Serializable> {
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
Co nie jestem zadowolony, że nie możemy używać interfejsy, takie jak listy, mapa przy przedłużaniu tej klasy:
public class CachedMap extends AbstractCachedSupport<Map<String, Integer>>
ponieważ nie rozciągają Serializable, więc musimy zawsze używać konkretnych klas:
public class CachedMap extends AbstractCachedSupport<HashMap<String, Integer>>
Nie trzeba dodawać, że ma to swój udział w problemach, na przykład podczas migracji z jednej konkretnej klasy do drugiej. Nie jest to też coś, co nazwałbym najlepszą praktyką, ale może to tylko ja.
Alternatywą, która daje nam elastyczność pracy z interfejsami byłoby usunąć ograniczone pisanie i sprawdzić przy starcie czy T rozciąga Serializable:
public abstract class AbstractCachedSupport<T> {
public AbstractCachedSupport() {
final Class<T> type = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
if (!Serializable.class.isAssignableFrom(type)) {
throw new RuntimeException("T must extend Serializable");
}
}
protected T get(CacheKey key, Supplier<T> supplier) {...}
// ...
}
To pozostawia nas bez kompilacji sprawdzanie na T rozszerzenie Serializable, też nie jest to miłe.
Czy znasz inny sposób, w jaki możemy rozwiązać ten problem w sposób elegancki? Czy wolisz używać pierwszego (ograniczonego parametru typu) lub drugiego (tylko sprawdzanie w czasie wykonywania)?
Kompromis byłby pójść na pierwszy i zawsze użyć klasy pojemnik do przechowywania kolekcji:
public class IntegerParamsContainer implements Serializable {
private static final long serialVersionUID = 1L;
private Map<String, Integer> map;
}
public class CachedMap extends AbstractCachedSupport<IntegerParamsContainer>
Ale to musi w końcu ten sam problem co drugie: nie ma czasu kompilacji sprawdzanie , odpowiedzialność spoczywa na barkach programistów, aby zawsze korzystali z kolekcji, które implementują Serializable.
Edit: Część zajęć rozciągających AbstractCachedSupport
są Wiosna (wersja 4,1 obecnie) zajęcia składników i nie są one nazywane CachedMap
lub coś podobnego, raczej CityAutocompleteDataBean
, WrParamsDataBean
etc .. Jeśli dodamy do tych klas generycznych komponentów będziemy skończyć z deklaracji, takich jak:
@Inject
private CityAutocompleteDataBean<ArrayList<String>>;
@Inject
private WrParamsDataBean<HashMap<String, WrData>>;
w przeciwieństwie do
@Inject
private CityAutocompleteDataBean;
@Inject
private WrParamsDataBean;
Przyczyna użycia <ArrayList<String>>
i <HashMap<String, WrData>>
pozwoli uciec większości programistów, gdy zobaczą takie linie kodu. Uważam też, że jest to dość brzydkie, biorąc pod uwagę, od czego zaczęliśmy i czego używamy.
Mimo to działa to zgodnie z wnioskiem, dziękuję Jesper.
Na marginesie, nie sądzę prace kontrolne run-time, czyli jeśli masz 'klasa CachedMap rozciąga AbstractCachedSupport