Кастинг, instanceof и @SuppressWarnings ( "unchecked" ) являются шумными. Было бы неплохо набить их в метод, в котором им не нужно будет смотреть. CheckedCast.castToMapOf()
- попытка сделать это.
castToMapOf()
делает некоторые предположения:
- (1) На карту нельзя доверять однородный
- (2) Редизайн для избежать необходимости кастинга или экземпляра не является жизнеспособным
- (3) Обеспечение безопасности типа в fail early является более важным, чем производительность
- (4) Возврат
Map<String,String>
является достаточным (вместо возвратаHashMap<String, String>
) - (5) Аргументы типа и значения типа не являются общими (например,
HashMap<String, ArrayList<String>>
)
(1), (2) и (3) являются симптомами моей рабочей среды, вне моего контроля. (4) и (5) являются компромиссами, которые я сделал, потому что я еще не нашел хороших способов их преодоления.
(4) Трудно преодолеть, потому что даже если a HashMap.class
было передано в Class<M>
, мне не удалось выяснить, как вернуть M<K, V>
. Поэтому я возвращаю Map<K, V>
.
(5) Вероятно, является неотъемлемым ограничением использования Class<T>
. Мне бы хотелось услышать альтернативные идеи.
Несмотря на эти ограничения, вы можете увидеть какие-либо проблемы с этим кодом? Я делаю какие-либо предположения, которые я не определил? Есть лучший способ сделать это? Если я изобретаю колесо, то указывайте мне на колесо.:)
public class CheckedCast {
public static final String LS = System.getProperty("line.separator");
/** Check all contained items are claimed types and fail early if they aren't */
public static <K, V> Map<K, V> castToMapOf(
Class<K> clazzK,
Class<V> clazzV,
Map<?, ?> map) {
for ( Map.Entry<?, ?> e: map.entrySet() ) {
checkCast( clazzK, e.getKey() );
checkCast( clazzV, e.getValue() );
}
@SuppressWarnings("unchecked")
Map<K, V> result = (Map<K, V>) map;
return result;
}
/** Check if cast would work */
public static <T> void checkCast(Class<T> clazz, Object obj) {
if ( !clazz.isInstance(obj) ) {
throw new ClassCastException(
LS + "Expected: " + clazz.getName() +
LS + "Was: " + obj.getClass().getName() +
LS + "Value: " + obj
);
}
}
public static void main(String[] args) {
// -- Raw maps -- //
Map heterogeneousMap = new HashMap();
heterogeneousMap.put("Hmm", "Well");
heterogeneousMap.put(1, 2);
Map homogeneousMap = new HashMap();
homogeneousMap.put("Hmm", "Well");
// -- Attempts to make generic -- //
//Unsafe, will fail later when accessing 2nd entry
@SuppressWarnings("unchecked") //Doesn't check if map contains only Strings
Map<String, String> simpleCastOfHeteroMap =
(Map<String, String>) heterogeneousMap;
//Happens to be safe. Does nothing to prove claim to be homogeneous.
@SuppressWarnings("unchecked") //Doesn't check if map contains only Strings
Map<String, String> simpleCastOfHomoMap =
(Map<String, String>) homogeneousMap;
//Succeeds properly after checking each item is an instance of a String
Map<String, String> checkedCastOfHomoMap =
castToMapOf(String.class, String.class, homogeneousMap);
//Properly throws ClassCastException
Map<String, String> checkedCastOfHeteroMap =
castToMapOf(String.class, String.class, heterogeneousMap);
//Exception in thread "main" java.lang.ClassCastException:
//Expected: java.lang.String
//Was: java.lang.Integer
//Value: 1
// at checkedcast.CheckedCast.checkCast(CheckedCast.java:14)
// at checkedcast.CheckedCast.castToMapOf(CheckedCast.java:36)
// at checkedcast.CheckedCast.main(CheckedCast.java:96)
}
}
Некоторое чтение я нашел полезным:
Общий factory с неизвестными классами реализации
Общие и параметризованные типы
Мне также интересно, если TypeReference/токены супертекста могут помочь с (4) и (5) и стать лучшим способом для решения этой проблемы. Если вы так думаете, отправьте пример.