Java中初始化ArrayList等对象时,何时应该使用菱形运算符<>?
问题根因
你遇到的编译报错本质和钻石运算符<>的使用无关,根源是你定义的Map使用了原始泛型类型丢失了类型信息:
你声明的Map是Map<String, List> res = new HashMap<>(),这里的List没有指定泛型参数,属于Java的原始类型(Raw Type),此时res.values()返回的集合类型是Collection<List>,而你要构造的ArrayList<List<String>>要求入参必须是Collection<? extends List<String>>,二者类型不匹配所以编译失败。
你使用new ArrayList(map.values())能正常运行,是因为你主动用了原始类型的ArrayList,跳过了编译期的泛型类型校验,本质是牺牲了类型安全换编译通过,只是你的业务逻辑中所有List存储的都是String元素,才没有触发运行时类型转换异常。
泛型
<>的使用规则 Java 7及以上版本引入的钻石运算符<>作用是简化泛型声明,只要编译器可以从上下文推断出泛型参数类型,就可以用<>省略右侧的类型声明,规则如下:
- 优先保证所有泛型类型的声明都指定完整参数,不要主动使用原始类型。你的场景只需要把Map的声明调整为
Map<String, List<String>> res = new HashMap<>(),存入元素时的new ArrayList()改为new ArrayList<>(),后续return new ArrayList<>(res.values())就可以正常编译,同时保留类型安全校验。 - 当编译器可以从左侧变量声明、方法返回值要求等上下文推断出泛型类型时,可以直接用
<>省略参数,比如List<String> list = new ArrayList<>()编译器可以从左侧声明推断出泛型为String,无需重复写。 - 当没有足够上下文供编译器推断类型时,需要显式指定泛型参数,比如直接用
var声明变量时,var list = new ArrayList<String>()必须指定参数,否则编译器会默认推断为ArrayList<Object>。 - 除非需要兼容Java 5之前的老代码,否则绝对不要主动使用不带
<>的原始泛型写法,原始类型会跳过所有泛型校验,很容易埋下运行时ClassCastException隐患。
修正后的可编译代码
public List<List<String>> groupAnagrams(String[] strs) { if (strs.length == 0) return new ArrayList<>(); Map<String, List<String>> res = new HashMap<>(); for (String s : strs) { char[] charArray = s.toCharArray(); Arrays.sort(charArray); String key = String.valueOf(charArray); if (!res.containsKey(key)) res.put(key, new ArrayList<>()); res.get(key).add(s); } return new ArrayList<>(res.values()); }
内容的提问来源于stack exchange,提问作者user15064722
相关产品推荐
相关产品推荐

