《深入理解 Scala》第三章 — 来点样式 — 编码规范

编码规范的目标可以归纳为三类:

  • 代码发现:作用在于使工程师能够轻松地推理代码,看懂其他工程师的意图。
  • 代码一致性:作用在于使整个项目的编码风格保持一致。
  • 错误防止:指那些有助于在生产代码里避免 bug 的样式规则。

设计编码规范应该采取的方法:

  1. 从错误防止规范开始;
  2. 开发一套代码发现相关的规范,应与团队使用的开发环境匹配;
  3. 最后是代码一致性规范,应考虑到自动化工具支持的需要。

空悬操作符是指一行代码的最后一个非空字符是一个操作符,有助于编译器判断语句真正结束的位置。

名称重整(name mangling):是指编译器修改或修饰类名或方法名,以便把它编译到底层的平台上。Scala 里名称重整用在嵌套类或辅助方法上。

当 Scala 必须生成匿名函数或匿名类时,Scala 会生成一个名字,包括函数或类所处的类名、字符串 annofun 和一个数字, 用 $ 符号连接起来。

编译器也会对默认参数应用名称重整措施。Scala 里默认参数也是被编码成方法的,方法名为 default 加上用来表示参数在函数里的出现顺序的序号。这出现在方法名字空间(namespace)里而不是类名字空间里。方法名$default$序号

参数位置语法是指定义参数的次序和调用时传递给参数的次序一致。可以对部分参数采用参数位置语法,对其余的采用命名参数语法。

Scala 为参数名使用静态类型或变量,但是它会动态修改默认值。名字是静态绑定的,值是动态绑定的

class Parent {
    def foo(bar: Int = 1, baz: Int = 2): Int = {println("super, bar:" + bar + ", baz:" + baz); bar + baz}
}

class Child extends Parent {
    override def foo(baz: Int = 3, bar: Int = 4): Int = {println("baz:" + baz + ", bar:" + bar); super.foo(baz, bar)}
}

val p = new Parent

println(p.foo())  // 3

val x = new Child
println(x.foo())  // 7

val y: Parent = new Child
println(y.foo())  // 7

println(x.foo(bar = 1))  // 4

println(y.foo(bar = 5, baz = 6))  // 11
// println(y.foo(5, bar = 6)) // error: parameter 'bar' is already specified at parameter position 1

Scala 里的命名参数是根据静态类型来决定参数的顺序。

总是标记覆盖(override)方法

override 关键字用于区分一个方法是否要覆盖还是重载一个方法。

纯抽象方法是指只有声明没有实现的方法。类首次定义时,Scala 不要求使用 override 关键字。

类线性化指调用某个类的父类方法时的调用顺序。

trait Animal {
    def talk: String
}

trait Cat extends Animal {
    override def talk: String = "Meow"
}

trait Dog extends Animal {
    override def talk: String = "Wookf"
}

val kittydoggy = new Cat with Dog
println(kittydoggy.talk)

val kitdog = new Dog with Cat
println(kitdog.talk)

如果不实用 override 关键字,将编译错误:inherits conflicting members:

对期望的优化进行标注

尾递归是指方法在最后一个语句调用自身。编译器会把尾递归优化为运行时的循环,而不是递归调用。优化的主要作用是避免栈溢出错误而不是提高速度。

tableswitch 字节码是一种比多重分支语句更有效率的分支语句。

要想 Scala 应用 tableswitch 优化,必须满足以下条件:

  • 匹配的值必须是个已知的整数;
  • 每个匹配语句都必须”简单“,不能包含类型检查、if 语句或抽取器。表达式的值必须在编译期可获得。
  • 应该有多于两个的 case 语句。
object TableSwitch {
    def func(x: Int) = x match {
        case 1 => "one"
        case 2 => "two"
        case z => z + "?"
    }
}

JVM 用 instanceof 字节码进行类、特质和对象的类型检查。Scala 必须把基础的整数打包成整数对象的形式来进行类型检查,这是 Scala 在 JVM 上对任何基础数据类型做类型检查的基本机制。

要优化尾递归调用,Scala 编译器需要以下条件:

  • 方法必须是 final 或私有的。不能多态。
  • 方法必须注明返回类型。
  • 方法必须在其某个分支的最后一句调用自身。

要求尾递归优化用 @tailrec 注解对方法进行标注。

Scala 用户应该确保:

  • 同行左大括号;
  • 使用悬空操作符或括号;
  • 使用有意义的名字;
  • 参数命名保持一致
  • 总是标记覆盖的方法。

发表评论

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

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