为何Java允许Comparator<String>的compare方法接收Object类型参数?
嘿,这个问题问到点子上了!这背后其实是Java泛型的类型擦除和原始类型兼容机制在起作用,我给你拆成几个关键点讲明白:
1. 你用了原始类型(Raw Type)的Comparator
你代码里把Comparator<String>类型的SORT_BY_LENGTH赋值给了Comparator c——这里的Comparator就是原始类型(没有指定泛型参数的版本)。Java引入泛型的时候,为了兼容Java 5之前的老代码,特意保留了原始类型的支持,但代价是会关闭泛型的类型检查机制,相当于回到了没有泛型的“裸奔”时代。
2. 类型擦除让compare方法的签名“悄悄变了”
在编译阶段,Java会对泛型进行类型擦除处理:把Comparator<String>里的<String>泛型标记完全擦除掉,对应的compare方法签名会从:
public int compare(String w, String v)
被改写为:
public int compare(Object w, Object v)
所以到了运行时,这个方法本来就接受Object类型的参数,你传入Object类型的变量完全符合方法签名要求,自然不会报错。
3. 实际对象的类型是String,所以逻辑能正常执行
虽然你声明o1和o2是Object类型,但它们实际指向的是String对象("abc"和"bc"都是String实例)。在compare方法内部调用length()时,JVM会自动把Object向下转型为String——因为实际对象就是String,所以转型成功,方法能正常执行并返回结果。
重要提醒:这种写法非常不安全!
这种代码虽然能运行,但编译器其实会抛出警告(如果你打开了完整的编译警告开关的话)。比如你要是把代码改成这样:
Object o1 = new Integer(123), o2 = "bc"; Comparator c = SORT_BY_LENGTH; System.out.println(c.compare(o1, o2));
运行时就会直接抛出ClassCastException,因为Integer不能转型为String去调用length()方法。
正确的实践方式
永远不要使用原始类型,应该始终用带泛型参数的声明:
Comparator<String> c = SORT_BY_LENGTH;
这样编译器会在编译阶段就检查参数类型,如果传入非String类型,直接编译报错,从根源上避免运行时的意外问题。
内容的提问来源于stack exchange,提问作者maycui

