OOP中的逆变和协变

逆变和协变在存在于强类型语言中,虽然很少提及,但是里面蕴含了面向对象的世界观。感谢和我一起讨论这个问题的人。

这里用了C#、Scala的语法作为示例,其实逆变和协变的概念跟语言本身关系不大,事实也是如此。

一、定义

逆变的参数可以由指定的类型的子类型代替,协变的参数可以由指定类型的父类型代替。

Scala中的逆变声明:Function[-A,+B] ;其中泛型-A为逆变类型,在实例化时,可以使用A类型或者A类的子类型。

二、协变与逆变的用途不同

1.语义

Scala中,函数的原型之一包含Function1[-A,+B],表示一个A类型的输入,B类型的输出(返回),替换的语法为A=>B

这个函数的定义就好像说,我需要类A帮我做一些事情,处理完之后给你一个B。而A可以完成的工作,其子类也应该能够完成,这正是里氏替换原则——父类出现的地方都可以用子类代替。逆变强调功能——“能做什么”。

顺便看下协变,输出为协变,表示我会给你你个B对象,如果B是肉,我当然可以说给了你食物,而食物是肉的父类,恰好是协变。如果使用逆变则说不通。协变强调类型——“是什么”。

2.刀和肉的例子

类型:食品<-肉<-牛肉。武器<-刀<-牛肉刀。(<-表示继承关系:父类<-子类)

情景:继续拿Function1做示例,如果Function1需要一把刀,会生产出肉。大致为Function(A):B普通刀(刀类)会生产出普通肉(肉类),牛肉刀会生产出牛肉。

问题:A的类型?B的类型?如何确定

A的类型可以为刀和牛肉刀,因为牛肉刀也是刀。甚至说刀的子类都能够满足条件——都有刀的功能。从继承来讲刀的子类都是刀。

所以A的类型应该为逆变——-刀(刀和子类)

因为做出的是肉,所以B类型肯定包含肉,但不确定是牛肉。所以我们可以设定返回为肉类型。

对于这个情景,我们对FunctionX的最终定义为:FunctionX(-刀):肉

没有协变?

我们没有看到协变,实际上在C#和Scala中,我们设定一个食品类型  来接收FunctionX的返回值也不会报错。因为所有的返回类型在语言中都被声明为协变了,也就是说实际的定义是FunctionX(-刀):+肉。这么做的原因是:如果我返回了一个肉,那么这个肉一定是食品,我总能用返回类型的父类型代替返回的对象。这种行为也是多态一方面的体现——在运行时改变了引用的实际类型。我认为,这是编译层面上的协变。


三、C#中的例子

ICompareable<in T>强调“可比较”这一功能,是逆变。

IEnumerable<out T>强调的是“可数的”类型,是协变。拿List<T>说明,List<肉>表示“我放了肉在列表里面”,也可以说"我放了食物在列表里面",即可以使用List<食品>代替。但是不能说“我放了牛肉在列表里面”,所以用List<牛肉>代替是不对的。

Java Scala 都是基于 JVM (Java 虚拟机) 平台的语言,它们之间有很多相似之处,但也存在着明显的差异。以下是两者的主要区别: ### 1. **编程范式** - **Java** 主要是面向对象编程(OOP),虽然也引入了一些函数式特性(如 Lambda 表达式等),但其核心设计仍然是围绕 OOP 展开的。 - **Scala** 则同时支持面向对象函数式的混合编程模式,允许开发者根据需求选择最适合的方式解决问题。它内置了许多高级功能来简化函数式编程模型的应用。 ### 2. **语法简洁度** - **Java** 的语法相对较为冗长,尤其是对于一些简单任务而言。例如声明量、创建匿名内部类等情况往往需要较多模板代码。 - **Scala** 强调表达力更强且更为紧凑的语法结构,减少了不必要的样板代码。比如可以直接用 `val` 或者 `var` 来快速定义不可或可值而无需指定具体的数据类型(因为类型推断机制可以自动识别)。 ### 3. **并发处理能力** - **Java** 提供了线程库 (`Thread`) 及相关的同步原语(锁、条件量等)。近年来随着 JDK 版本更新加入了 ForkJoinPool 等现代框架改进了并发性能。 - **Scala** 内置 Akka 框架的支持,这是一个强大的 actors-based 库用于构建高容错性的分布式系统。此外还有 Future/Promise API 让异步操作得更容易管理。 ### 4. **泛型与集合** - **Java** 泛型由于历史原因存在一定的局限性复杂性,特别是涉及到逆变等问题时容易让人感到困惑。 - **Scala** 具有更加完善直观的泛型体系,包括上下界限制、视图界定等功能使得程序员能够写出更具灵活性及安全性的通用组件。 ### 5. **社区支持** - **Java** 自从发布以来就占据了企业级市场的主导地位,拥有庞大的用户基数技术生态系统,各种第三方库极其丰富,并持续得到 Oracle 官方团队维护发展。 - **Scala** 社区规模较小但活跃度很高,尤其受大数据领域青睐(Apache Spark 就是用 Scala 开发)。不过相比 Java 生态略显单薄一点。 总结来说,如果你正在寻找一种适合大型项目并且已经被广泛验证过的稳定技术栈,那么 Java 是非常好的选项之一;而对于那些希望通过更优雅精简的方式来完成特定计算密集型工作负载的人来说,Scala 明确展现出了它的优势所在。 --- 如果您有兴趣了解更多有关这两种语言的信息或是探讨其他相关话题,请参考下列建议的问题列表:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值