您也可以为地图创建一个装饰器。它不能避免为键创建一个新类,但是它会隐藏它,因此地图的类型为Map<GeoData,Integer>
。当然,如果您执行keySet()
等操作,城市将始终为空,因为此信息已丢失(它不是键的一部分)。
public class Country {}
public class Region {}
public class City {}
public class GeoData {
public final Country country;
public final Region region;
public final City city;
public GeoData(Country country, Region region, City city) {
this.country = country;
this.region = region;
this.city = city;
}
}
public class InhabitantsInRegion implements Map<GeoData, Integer> {
private static class InnerKey {
private final Country country;
private final Region region;
private InnerKey(Country country, Region region) {
this.country = country;
this.region = region;
}
}
private final Map<InnerKey, Integer> map = new HashMap<>();
@Override
public int size() {
return map.size();
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public boolean containsKey(Object key) {
return key instanceof GeoData && map.containsKey(newInnerKey((GeoData) key));
}
@Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
@Override
public Integer get(Object key) {
if (key instanceof GeoData) {
return map.get(newInnerKey((GeoData) key));
}
return null;
}
@Override
public Integer put(GeoData key, Integer value) {
return map.put(new InnerKey(key.country, key.region), value);
}
@Override
public void putAll(Map<? extends GeoData, ? extends Integer> m) {
m.entrySet().forEach(entry -> put(entry.getKey(), entry.getValue()));
}
@Override
public Integer remove(Object key) {
if (key instanceof GeoData) {
return map.remove(newInnerKey((GeoData) key));
}
return null;
}
@Override
public void clear() {
map.clear();
}
@Override
public Set<GeoData> keySet() {
return map.keySet().stream()
.map(InhabitantsInRegion::newGeoDataKey)
.collect(toSet());
}
@Override
public Collection<Integer> values() {
return map.values();
}
@Override
public Set<Entry<GeoData, Integer>> entrySet() {
return map.entrySet().stream()
.map(this::newEntry)
.collect(toSet());
}
private Entry<GeoData, Integer> newEntry(Entry<InnerKey, Integer> entry) {
return new Entry<GeoData, Integer>() {
@Override
public GeoData getKey() {
return newGeoDataKey(entry.getKey());
}
@Override
public Integer getValue() {
return entry.getValue();
}
@Override
public Integer setValue(Integer value) {
return map.put(entry.getKey(), value);
}
};
}
private static InnerKey newInnerKey(GeoData geoDataKey) {
return new InnerKey(geoDataKey.country, geoDataKey.region);
}
private static GeoData newGeoDataKey(InnerKey innerKey) {
return new GeoData(innerKey.country, innerKey.region, null);
}
}