Java中对应Kotlin协变(out)与逆变(in)修饰符的实现方式
Java中泛型协变(out)与逆变(in)的实现方式
Java没有Kotlin中直接在泛型参数声明处标注out/in的语法,而是通过通配符和方法签名约束来实现类似的变体效果,以下是具体实现方式:
一、协变(对应Kotlin的out)
协变适用于泛型类型作为生产者(仅用于返回值,不用于方法参数)的场景,保证子类类型的泛型实例可以安全赋值给父类类型的泛型引用。
1. 使用处协变(通配符? extends)
在使用泛型时,通过? extends 上限类型声明协变:
// 可以将Box<SubType>安全赋值给Box<? extends SomeType> Box<? extends SomeType> box = new BoxImpl<SubType>(new SubType());
2. 定义支持协变的接口
要让接口本身具备协变特性(让Kotlin能识别并使用out T实现),需要确保接口的泛型参数T仅出现在方法的返回值位置,不允许作为方法参数:
public interface Box<T extends SomeType> { // 仅作为返回值,符合生产者特性 T getItem(); // 禁止添加此类方法,否则T会成为消费者,破坏协变 // void setItem(T item); }
此时Kotlin实现该接口时,使用out T的报错是因为Java泛型默认是不变的,但由于接口仅暴露生产者方法,这个协变实现是安全的,可以添加@Suppress("VARIANCE_MISMATCH")忽略编译器提示,或者直接去掉out(实际使用时可通过Java通配符实现协变)。
二、逆变(对应Kotlin的in)
逆变适用于泛型类型作为消费者(仅用于方法参数,不用于返回值)的场景,保证父类类型的泛型实例可以安全赋值给子类类型的泛型引用。
1. 使用处逆变(通配符? super)
在使用泛型时,通过? super 下限类型声明逆变:
// 可以将Box<SomeType>安全赋值给Box<? super SubType> Box<? super SubType> box = new BoxImpl<SomeType>();
2. 定义支持逆变的接口
要让接口本身具备逆变特性,需要确保接口的泛型参数T仅出现在方法的参数位置,不允许作为返回值:
public interface ConsumerBox<T extends SomeType> { // 仅作为方法参数,符合消费者特性 void setItem(T item); // 禁止添加此类方法,否则T会成为生产者,破坏逆变 // T getItem(); }
此时Kotlin实现该接口时,可以直接使用in T,编译器会识别到T仅作为输入,支持逆变。
内容的提问来源于stack exchange,提问作者Matthew Layton
相关产品推荐
相关产品推荐

