为何未使用的类泛型参数<X>会影响WParam.unwrap()方法的返回类型?
未使用的类泛型参数为何会影响方法的泛型返回类型?
先看你给出的代码场景:
public class TestClass { void test() { final Long val1 = new Simple().unwrap(new Wrapper<>(5L)); // OK final Long val2 = new WParam().unwrap(new Wrapper<>(5L)); // ERROR: Object cannot be converted to Long final Long val3 = new WParam<>().unwrap(new Wrapper<>(5L)); // OK final Long val4 = new WParam<Byte>().unwrap(new Wrapper<>(5L)); // OK } } record Wrapper<O>(O obj) {} class Simple { public <T> T unwrap(Wrapper<T> wrapper) { return wrapper.obj(); } } class WParam<X> { public <T> T unwrap(Wrapper<T> wrapper) { return wrapper.obj(); } }
核心原因是原始类型的泛型擦除规则:
当你使用new WParam()这种方式创建实例时,你创建的是原始类型(没有指定泛型参数的泛型类实例)。Java编译器对原始类型有特殊处理:原始类型的所有泛型方法都会被强制擦除泛型参数,方法的泛型类型会被替换为其类型上限(这里<T>没有指定上限,所以替换为Object)。
这就导致new WParam().unwrap(...)的返回值被视为Object,无法直接赋值给Long变量,触发编译错误。
逐个解析不同场景:
new Simple().unwrap(...):Simple不是泛型类,不存在原始类型的问题,编译器可以正常根据传入的Wrapper<Long>推断出方法的泛型参数T=Long,返回Long类型,赋值正常。new WParam().unwrap(...):WParam是泛型类,但这里用了原始类型,类中所有泛型方法的泛型都被擦除,unwrap方法的返回类型变为Object,无法直接转为Long,报错。new WParam<>().unwrap(...):使用菱形运算符后,编译器会为WParam推断出一个泛型类型(这里无上下文约束,会推断为Object),但关键是这个实例不再是原始类型。此时unwrap方法的泛型推断可以正常工作,根据Wrapper<Long>推断T=Long,返回Long,赋值正常。new WParam<Byte>().unwrap(...):明确指定了WParam的泛型参数X=Byte,实例不是原始类型,unwrap方法的泛型推断不受X的影响(因为X和方法的T完全无关),正常推断T=Long,返回Long,赋值正常。
总结
只要泛型类被当作原始类型使用(不指定任何泛型参数),类内所有泛型方法的泛型都会被强制擦除,不管类的泛型参数是否被实际使用。只要避免原始类型(哪怕用菱形运算符<>, 或者通配符<?>),泛型方法的类型推断就能正常运行。
内容的提问来源于stack exchange,提问作者Marián Petráš
相关产品推荐
相关产品推荐

