为何不使用Set<String> set = new HashSet()替代Set<String> set = new HashSet<String>()?
Great question! Let's break this down clearly to understand why the two approaches differ, and whether your shorter version is "enough" for your use case.
首先,两种写法的本质区别
大家常用的写法:
Set<String> set = new HashSet<String>();
这里的HashSet<String>是参数化泛型类型,明确指定了集合只能存储String类型,编译器会在编译阶段严格检查所有对这个集合的操作,确保不会存入非String类型的元素。你的写法:
Set<String> set = new HashSet();
这里的new HashSet()创建的是原始类型的集合(没有指定泛型参数),虽然左边的Set<String>是泛型类型,编译器会帮你做一个“未检查的转换”让代码能运行,但这个过程会绕过部分泛型的类型安全检查。
为什么你的写法看起来能实现类型检查?
当你用原始类型的HashSet赋值给泛型的Set<String>时,编译器会在编译时弹出一个Unchecked assignment警告(开启IDE警告提示就能看到)。此时你直接通过set变量操作集合(比如set.add("hello")),因为左边的Set<String>是泛型,编译器会检查你添加的元素类型是否符合String要求。
但这里藏着个隐患:如果后续代码通过原始类型引用操作这个集合(比如强制转换为HashSet后添加非String元素),编译器不会报错,但当你从Set<String>中取出元素时,会在运行时抛出ClassCastException——这个错误本应该在编译阶段就被拦截。
你的写法是否“足够”?
如果你能百分百保证永远不会通过任何原始类型引用往这个集合里加非String元素,从功能上来说它确实能工作。但从代码健壮性和Java最佳实践的角度,它并不“足够”:
- 忽略编译器警告是不良习惯,警告往往是潜在bug的提示;
- 原始类型写法会降低代码可读性,其他开发者看到时会疑惑你是否是故意放弃泛型安全;
- 一旦后续代码修改引入了非
String元素,错误会推迟到运行时,排查成本更高。
更优的简洁写法
从Java 7开始,支持菱形语法(Diamond Operator),它既能保持你想要的简洁,又能保留泛型的类型安全:
Set<String> set = new HashSet<>();
编译器会根据左边的Set<String>自动推断出右边HashSet的泛型参数是String,没有警告,也完全符合类型安全要求,这是现在最推荐的写法。
内容的提问来源于stack exchange,提问作者Just Someone

