去年我加入水滴团队, 面试中, 面试官问: 你了解 Scala 吗?
不了解 (尴尬)
你知道 Spark 吗? 它就是使用 Scala 编写的, 不过在我们团队中, Scala 主要作为后端语言, 我们 90% 以上的业务代码都是使用 Scala 编写 Scala 在国内使用的比较少, 但是在国外用的还是蛮多的, 如 Twitter 就是使用 Scala 写的后端
自那以后, 我便开始了 Scala 的学习之旅
Scala 是由德国的计算机科学家和编程方法教授 Martin Odersky 设计出来的, 它的设计原理严格遵循数学的逻辑推理因此它是一门优秀的编程语言, 它不仅仅在工业界被广泛使用, 在学术界也占用很高的研究地位
由于之前的 Java 背景, 我经常拿 Scala 与 Java 这两门语言比较 Scala 和 Java 都基于 JVM, 因此 Java 的类库, Scala 都可以直接使用但是我对 Scala 印象最深的点, 并不是面向对象, 而是它还拥抱函数式, 尤其是它的高阶
如果我们把面向对象比作站在地面上观察事物的原理, 并且使用这些原理解决问题, 那么高阶就是让你站在山上去看待事物, 对问题进行更高层次的抽象
因此不管是解决实际问题, 还是提高对编程语言的认知, Scala 都是一们值得学习的语言
我是从快学 Scala 这本书开始学习 Scala 的, 受此书启发, 我想能不能书写一个从 Java 到 Scala 系列, 寻找一棵从 Java 通往 Scala 的连续的知识树, 通过对知识树的讲解, 来学习 Scala
好了, 这就是本系列的第一篇, 那么我们如何谈起呢?
既然 Java 和 Scala 都是面向对象的, 那我们就来探索一下什么是面向对象吧
模板和对象
模板是在代码层面描述一类对象的行为或者状态的代码, 它是抽象的如 Java 中的类, C 语言中的结构体, 它们都是模板
对象是在运行期间通过模板在内存中生成的一个个实体, 它是具体的如 Java 在运行期间通过 new 在内存中产生的实体就叫做对象
如果你说共享单车, 那么它就是一个模板; 如果你说这辆共享单车和那辆共享单车, 那它们就是对象
在代码层面, 对象的行为可以定义为方法, 对象的状态可以定义为属性, 那我们如何去描述一类对象的方法或者属性呢?-封装
例如共享单车, 它有车轮, 二维码等属性, 有开锁和关锁等行为那么我们可以有三种方式来封装共享单车
基于对象的封装
这种方式就是直接封装, 最典型的例子就是 C 语言中的结构体 封装共享单车的模板如下:
struct SharedBicycle{
车轮;
二维码;
开锁;
关锁;
};
基于类的封装
大多数面向对象的语言, 如 Java,Scala,C++ 等, 都使用这种方式封装, 模板如下:
class SharedBicycle{
属性: 车轮;
属性: 二维码;
方法: 开锁;
方法: 关锁;
}
基于原型的封装
JavaScript 就是使用这种封装方式, 模板如下:
function SharedBicycle(){
this. 车轮 = xxx;
this. 二维码 = xxx;
- }
- // 添加原型方法
SharedBicycle.prototype. 开锁 = function(){...};
SharedBicycle.prototype. 关锁 = function(){...};
纯面向对象
我们已经得知, 可以用多种实现面向对象的不同技术, 那么什么是纯面向对象的语言呢?
我们知道 Java 是一门面向对象的语言, 那么在 Java 中是否真的万物皆对象?
在 Java 中, 我们可以写这么一段代码 int a = 3; 然后我们发现 a 并没有封装任何的属性或者方法
因此我们可以说 a 不是一个对象, Java 不是一门纯粹面向对象的语言
再看看 Scala , 不论是低阶的 Int,Double, 还是高阶类型, 都封装有属性或者方法, 因此 Scala 才是一门纯粹面向对象的语言
那么是什么支持 Scala 一切皆为对象的呢?-Scala 的通用类型系统
Scala 通用类型系统
顶类型
我们知道, 在 Java 中, 所有对象的顶类型都是 java.lang.Object, 但是 Java 却忽略了 int,double 等 JVM 原始类型, 它们并没有继承 java.lang.Object
但是在 Scala 中, 存在一个通用的顶类型-Any
Scala 引入了 Any 作为所有类型共同的顶类型 Any 是 AnyRef 和 AnyVal 的超类
AnyRef 面向 Java(JVM) 的对象世界, 它对应 java.lang.Object , 是所有对象的超类
AnyVal 则代表了 Java 的值世界, 例如 int 以及其它 JVM 原始类型
正是依赖这种继承设计, 我们才能够使用 Any 定义方法, 同时兼容 scala.int 以及 java.lang.String 的实例
- class Person
- val allThings = ArrayBuffer[Any]()
- val myInt = 42 // Int, kept as low-level `int` during runtime
- allThings += myInt // Int (extends AnyVal)
- allThings += new Person() // Person (extends AnyRef), no magic here
正是通过这种通用类型系统的设计, 使得 Scala 摆脱原始类型这种边缘情况的纠缠, 从而实现纯粹的面向对象
说完了顶类型, 我们再来看看底类型
底类型
我们知道在 Java 中比较闹心的就是异常处理, 当我们调用一个抛出异常的方法, 我们必须抛出或者处理异常
但是在 Scala 中, 我们知道一切表达式皆有类型, 难道抛异常也是有类型的?
- scala> val a = Try(throw new Exception("123"))
- a: scala.util.Try[Nothing] = Failure(java.lang.Exception: 123)
我们发现抛异常竟然是 Nothing 类型, 在 Scala 中, 难道 Nothing 仅仅是作为抛异常的类型?
- scala> def fun(flag:Boolean)={
- if(flag){
- 1 // Int
- }else{
- throw new Exception("123") //Nothing
- }
- }
- fun: (flag: Boolean)Int
我们发现 fun 函数并没有报错, 而且返回值类型竟然是 Int, 这让我们有一个大胆的猜测: Nothing 是 Int 的子类型
- [Int] -> ... -> AnyVal -> Any
- Nothing -> [Int] -> ... -> AnyVal -> Any
其实在 Scala 中, Nothing 不仅仅是 Int 的子类型, 它更是所有类型的子类型 这让我们又产生了一个大胆的猜测: 难道 Nothing 继承了所有的类型? 咳咳, 这个问题我们以后在讨论
在 Scala 中, 还有一个类型 Null 遵循着和 Nothing 一样的原理
- scala> def fun2(flag:Boolean)={
- if(flag){
- "123" //String
- }else{
- null //Null
- }
- }
- fun2: (flag: Boolean)String
同理, 我们可以得出 Null 是 String 的子类型
- [String] -> AnyRef -> Any
- Null -> [String] -> AnyRef -> Any
那我们看看 Null 是否可以兼容 Int
- scala> def fun3(flag:Boolean)={
- if(flag){
- 123 //Int
- }else{
- null //Null
- }
- }
- fun3: (flag: Boolean)Any
我们发现 fun3 的返回值类型竟然是 Any, 说明 Null 不能兼容 Scala 的值类型, 其实从 Scala 的帮助手册中我们就可以得出结论: Null 是所有引用类型的子类型
abstract final class Null extends AnyRef
正因如此, fun3 的返回值类型才是 Any, 因为 Any 才是 AnyVal 和 AnyRef 公共的超类
总结
本文以面向对象为引子, 找到了一个 Java 和 Scala 共有的知识节点, 从而引出 Scala 的通用类型系统那么在下一篇文章中, 我们由此展开进一步思考, 到底什么是所谓的类型, 以及 Scala 在类型方面存在哪些与 Java 不同的有趣的地方
Scala 类型的类型 (一)
来源: https://juejin.im/post/5a9cd6736fb9a028b5475fa4