[英]How to design a method that has no `unchecked cast` warning in java
我有一个名为Myclass
的 class ,它只是HashMap
的包装器,我希望能够存储下面列出的可能的键/值对:
KEY_A
-> MyClassA
KEY_LIST_B
-> List<MyClassB>
KEY_C
-> List<MyClassC>
这是我的代码:
public class Main {
public static void main(String[] args) {
MYClass myClass = new MYClass();
myClass.set(MyEnum.KEY_A, new MyClassA());
myClass.set(MyEnum.KEY_LIST_B, new ArrayList<>(Arrays.asList(new MyClassB())));
myClass.set(MyEnum.KEY_C, new ArrayList<>(Arrays.asList(new MyClassC())));
MyClassA a = (MyClassA) myClass.get(MyEnum.KEY_A);
List<MyClassB> listB = (List<MyClassB>) myClass.get(MyEnum.KEY_LIST_B);//Unchecked cast
List<MyClassC> listC = (List<MyClassC>) myClass.get(MyEnum.KEY_C);//Unchecked cast
}
public static class MYClass {
private final HashMap<MyEnum, Object> map;
public MYClass() { map = new HashMap<>(); }
public Object get(MyEnum key) { return map.get(key); }
public void set(MyEnum key, Object value) { map.put(key, value); }
}
public static class MyClassA {}
public static class MyClassB {}
public static class MyClassC {}
public enum MyEnum {KEY_A, KEY_LIST_B, KEY_C}
}
如何设计(这些方法的签名) MyClass
的get()
和set()
方法,以便能够存储前面列出的键/值对并避免Unchecked cast
?
为什么即使演员表不安全,这条线也没有Unchecked cast
表警告?
MyClassA a = (MyClassA) myClass.get(MyEnum.KEY_A);
一个典型的解决方案是使用一个通用的“键类”,它携带有关值类型的信息。 键的实例将引用值类型的Class
。 请注意,您的枚举接近此解决方案,但不幸的是,您不能有一个通用枚举,其中每个常量都有不同的类型 arguments。
这是一个例子:
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Container {
public static final Key<Foo> FOO_KEY = new Key<>(Foo.class);
@SuppressWarnings({"rawtypes", "unchecked"})
public static final Key<List<Bar>> BAR_LIST_KEY = new Key(List.class);
@SuppressWarnings({"rawtypes", "unchecked"})
public static final Key<List<Baz>> BAZ_LIST_KEY = new Key(List.class);
private final Map<Key<?>, Object> map = new HashMap<>();
public <T> void put(Key<T> key, T value) {
map.put(key, value);
}
public <T> T get(Key<T> key) {
var value = map.get(key);
return value == null ? null : key.getValueType().cast(value);
}
public static final class Key<T> {
private final Class<T> valueType;
private Key(Class<T> valueType) {
this.valueType = valueType;
}
public Class<T> getValueType() {
return valueType;
}
}
}
我个人会接受这个实现,因为(被抑制的)警告只发生在Container
内。 使用Container
class 的代码不会遇到未经检查的强制转换或原始类型警告。
Class
@SuppressWarnings
合法地表示参数化类型。 换句话说,您可以拥有Class<List>
但不能拥有Class<List<Foo>>
1 。 但是,如果您想避免甚至抑制这些警告,那么我能想到的唯一方法是为您的通用值类型创建包装类。 如:
public record MyClassBList(List<MyClassB> list) {}
然后有一个Key<MyClassBList>
。
至于为什么:
MyClassA a = (MyClassA) myClass.get(MyEnum.KEY_A);
不会导致省略“未经检查的强制转换”警告,这是因为不涉及 generics。 如果在运行时无法进行上述转换,则会ClassCastException
。 但是当涉及 generics 时,即使您“更改”类型 arguments,强制转换也可能成功。 例如:
Map<String, Object> map = new HashMap<>();
List<String> stringList = new ArrayList<>();
stringList.add("Hello, World!");
map.put("key", stringList);
// This results in an "unchecked cast" warning, but the cast
// **will** succeed at run-time.
List<Integer> integerList = (List<Integer>) map.get("key");
integerList.add(0); // okay, because you've said it's a List<Integer>
// Now 'stringList' has a String element AND an Integer element
for (String element : stringList) {
// this will fail at run-time (after the cast to List<Integer>
// was successful) because the second iteration will result in
// trying to cast an Integer to a String
System.out.println(element);
}
而这种混乱的情况就是为什么他们警告你“未经检查的演员表”。 由于put
和get
的定义方式,上面的Container
示例应该不会导致此类问题,除非您故意和明确地尝试破坏事物(例如,通过使用原始类型、强制转换“hacks”等)。
1.从技术上讲,您可以参考Class<List<Foo>>
,方法类似于我在上面对Key
所做的操作,但它实际上并不代表List<Foo>
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.