《深入理解 Scala》第二章 — 核心规则

在 REPL 里做实验

优先擦用面向表达式编程

表达式返回值;语句执行代码,但不返回值。Scala 大部分语句都返回其最后一个表达式的值作为结果。

  • 不变性(immutability):指对象或变量赋值后就不再改变状态。
  • 可变性(mutability):指对象或变量在其生命周期中能够被改变或操纵。

优先选择不变性

Scala 里的所有变量都是指向对象的引用。把变量声明为 val 参数意味着它是个不变的“引用”。

所有的方法参数都是不变引用,类参数默认是不变引用。

创建可变引用的唯一方法是使用 var 引用。

Scala 里不变性很重要,它有助于程序员推理代码,在定义判等或写并发程序时尤其明显。

默认情况下,Scala 用对象位置判等法和散列。对象位置判等法用对象在内存中的位置来作为判等的唯一因素。

Scala 提供了 ## 方法来对应 hashCode 方法,== 方法来对应 equals 方法。hashCode 和 equals 方法总是成对实现。

在实现判等时一般推荐确保如下约束:

  • 如果两个对象相等,它们的散列值应该也相等;
  • 一个对象的散列值在对象生命周期中不应该变化;
  • 在把对象发送到另一个 JVM 时,应该用两个 JVM 里都有的属性来判等。

满足上述要求的唯一方法就是使用不变对象。

不变对象可以安全地在多个线程之间传递而不用担心争用。能够消除锁以及锁带来的各种潜在 bug,极大提供代码库的稳定性。

使用 Option 不用 null

Option 可以视作容器,里面要么有东西(Some),要么什么都没有(None)。

在 Scala 里,参数类型为 Option 表示参数可能是未初始化的。Scala 的惯例是不要使用 null 或未初始化的参数传给函数。

多态场景下的判等

对于需要比引用判等更强的判等的类,最好避免多层实体类层次。

在 JVM 里实现 equals 时,一般来说在做深度判等前先判断引用是否相等的性能更高。设计好的 equals 方法的另一个常用范式是(在深度判等之前)用 hashCode 做个早期判断。

多态判等实现一般继承 scala.Equals 特质,覆写 canEqual 方法,和 equals 方法串联起来使用,使子类可以跳出(opt-out)其父类的判等实现。(注:其实就是个回调的钩子)

trait InstantaneousTime extends Equals {
    val repr: Int
    override def canEqual(other: Any) = other.isInstanceOf[InstantaneousTime]

    override def equals(other: Any): Boolean =
        other match {
            case that: InstantaneousTime =>         //  通过 模式匹配 做了  this -> that  的类型判等
                if (this eq that) true else {
                    (that.## == this.##) && (that canEqual this) && (repr == that.repr)      //  通过 canEqual 做了  that -> this  的类型判等
                }
            case _ => false
        }

    override def hashCode(): Int = repr.hashCode
}

trait Event extends InstantaneousTime {
    val name: String
    override def canEqual(other: Any) = other.isInstanceOf[Event]

    override def equals(other: Any): Boolean = other match {
        case that: Event =>      //  通过 模式匹配 做了  this -> that  的类型判等
            if (this eq that) {
                true
            } else {
                (that canEqual this) && (repr == that.repr) && (name == that.name)    //  通过 canEqual 做了  that -> this  的类型判等
            }   
        case _ => false   
    }
}

上面的示例都是做了双向的类型判等,使比较的实例必须是同一类型的,屏蔽了父类和子类。


欢迎关注我的微信公众号: coderbee笔记,可以更及时回复你的讨论。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据