协变 vs 逆变

逆变和协变都是类型安全产生的概念;

其实就是:

协变:让返回的类型可以是子类型 —— 输出

逆变:让参数可以是父类型 —— 输入

@interface RACCommand<__contravariant InputType, __covariant ValueType> : NSObject
可以看到这样的定义

协变(covariance)和逆变(contravariance)在 Objective-C 泛型中并不十分常见,但它们是理解类型系统中的灵活性和安全性的重要概念。以下是协变和逆变的概念和示例:

协变(Covariance)

协变允许将子类对象赋值给父类泛型对象(从具体到通用)。这在返回类型中尤其有用,因为可以向上兼容。例如,如果我们有一个 RACSignal 类的子类 RACSubject,协变可以让 RACSignal<NSString *> 赋值给 RACSignal<id>

@interface RACSignal<__covariant ValueType> : NSObject
@end

RACSignal<NSString *> *stringSignal = [[RACSignal alloc] init];
RACSignal<id> *anySignal = stringSignal; // 合法,因为 RACSignal 是协变的

这里,通过使用 __covariantRACSignal 能够将 RACSignal<NSString *> 赋值给 RACSignal<id>,保证类型的灵活性。

逆变(Contravariance)

逆变允许将父类泛型对象赋值给子类泛型对象(从通用到具体)。在 Objective-C 中使用 __contravariant 让泛型参数接受更广泛的类型,主要用于参数类型。例如:

@interface MyHandler<__contravariant InputType> : NSObject
- (void)handle:(InputType)input;
@end

在上面的代码中,MyHandler 的泛型参数 InputType 使用了 __contravariant,因此可以传递更广泛的类型作为参数:

MyHandler<id> *objectHandler = [[MyHandler alloc] init];
MyHandler<NSString *> *stringHandler = objectHandler; // 合法,逆变允许更具体的类型
[stringHandler handle:@"Hello, world!"];

这里,逆变使得 MyHandler<id> 可以赋值给 MyHandler<NSString *>,所以我们能够创建一个接受 id 类型的处理器,并将其传递给一个期待 NSString 类型的处理器。

总结

  • 协变 (__covariant):用于返回类型,使子类泛型可以赋值给父类泛型。

  • 逆变 (__contravariant):用于参数类型,使父类泛型可以赋值给子类泛型。

在实际开发中,协变和逆变让代码更灵活并提高了类型安全性。

Last updated