繁体   English   中英

如何设计在 java 中没有“未检查强制转换”警告的方法

[英]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}
}
  1. 如何设计(这些方法的签名) MyClassget()set()方法,以便能够存储前面列出的键/值对并避免Unchecked cast

  2. 为什么即使演员表不安全,这条线也没有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);
}

而这种混乱的情况就是为什么他们警告你“未经检查的演员表”。 由于putget的定义方式,上面的Container示例应该不会导致此类问题,除非您故意和明确地尝试破坏事物(例如,通过使用原始类型、强制转换“hacks”等)。


1.从技术上讲,您可以参考Class<List<Foo>> ,方法类似于我在上面对Key所做的操作,但它实际上并不代表List<Foo>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM