scala 笔记(4)

继承

每个 OO 语言使用的模型都不大一样。C++ 使用的模型就是多继承,这个概念上简单的东西却容易造成滥用和误解,其实不少复杂的结构通过多继承都能实现类似的概念,一些难掌握的概念包括:

  • virtual 继承,获得菱形结构的继承关系
  • by default C++ 并不使用 virtual 函数,因此需要 polymorphism 的话必须使用 virtual 标识
  • 通过 public/protected/private 标识继承类型可以表示继承或者实现等不同的语义
  • 不带有实现的 virtual 函数需要标识出来,这样对应的类成为纯虚,不能实例化

与 C++ 类似的可能就是 python,尽管没有那么多复杂的访问控制,python 也使用了 multiple inheritance,但使用了 linearization。相对 C++ 这样复杂的模型,Java 其实做了很大的简化,这包括

  • 默认的方法就是 virtual 的,子类就能覆盖并产生多态
  • 引入 abstract 关键字来标识 pure virtual 函数或者类
  • 引入单独的 interface 表示对象之间的协议,而这个协议不含有实现
  • 只允许单继承,但可以实现多个接口

Java 这种设计导致的结果是往往需要很多 thin interface,这样实现 interface 的任务才能简化,但带来的坏处是使用的人必须将这个简化的 set 翻译到 application 的 context 里面,除了实现某些多继承情况变得更繁复以外,最大的坏处莫过于使得代码变得非常冗余,好处的确让人觉得理解设计者意图起来比较轻松。scala 的设计应该算是对 Java 的一个重大的改变。除了 class 和 abstract class 这些 Java 已经有的概念以外,scala 引入了 override 关键字,要求重写方法的时候必须表明,同时也接受了 Java 的 final 关键字故意避免继承,另有

  • 提供了 object 将原先类里面的 static 部分,常用的就是 factory method 整合在这个类伴随的 object 里面,这个设计和 Java 不少类存在的复数形式类关系类似,比如 Collection 与 Collections,后者提供了一些对前者的补充;在 scala 里面甚至可以将一个 abstract class 的所有实现(一个封闭的继承体系)放在伴随的 object 里面作为 private class,这样用户只能通过 object 的 factory method 构造符合 abstract class 定义的实例进行事务,这样将 client code 与实现很容易分离开来
  • 提供了 trait 作为 mixin 的元素,所谓的 trait 与 class 的一个主要区别在于没有构造函数,对应的 super 调用是 dynamic binding 的,trait 可以包含成员和方法,乃至方法的实现,与 interface 相比相当于有可以利用的成员和默认的实现。类似 interface,一个类只能有一个父类但可以有多个 trait
  • trait 可以继承 class,这导致这样的 trait 只能与对应的类/子类进行 mixin,这样获得 trait 类似于 interceptor,或者说是个 decorator,多个 trait 修饰的时候就像洋葱一样一层一层的加上去,所以书写上最后一个 trait 是最先被调用的

Java/scala 都是用了 linearization,这意味着如果两个 interface 或者 class/interface、class/trait、trait/trait 有两个signature 完全一样的方法,最后的结果是只有一个:

  • 在 C++ 里面,可以通过类名加上方法区分多继承获得的同 signature 函数,但这被人认为是难以理解的
  • 在 Java 里面因为 interface 不含有实现,所以对应的类只可能有这个 signature 方法的一份实现,或者来自父类或者子类重写了
  • scala 里面这个情况更复杂,一般的形式是 extends . with .. with …,可以认为最外层的 trait 可以用内层的实现(通过 super),这称为 stackable modification

通过 mixin 可以实现 rich interface,这是因为很多情况下我们需要默认的实现,然后基于少数几个方法的实现,我们就能生成一堆相关联的方法,典型的例子是逻辑运算符,通常我们只需要提供 < 和 == 就行了,scala 提供了 Ordered 这个 trait。

在利用 scala 提供的这些语言特性组织代码的时候,除了 class、abstract class 和 trait 以外,还有所谓的 case class,这可以看成是简化的 class,即不需要写 new Foo 而可以直接写 Foo 并附带了一些方法的实现的特殊情况。那么比较难分清楚的往往是 abstract class 和 trait,

  • trait 一般是为了 re-use,并且不仅仅局限在一类对象上;如果希望 Java 能用只能写 pure trait(没有实现);super 是 dynamic binding,效率稍低
  • abstract class 一般是比较封闭的继承情况下重用;可以直接被 Java 重用;super 是 static binding,效率高

无参数的方法

scala 里面一个约定俗成的事情是如果一个 method 没有 side effect 且没有参数,对应的 def 的参数列表包括括号应该省略,这时候你将 def 替换成 val 对应的 client code 调用没有区别(否则def 的话需要带括号,哪怕是空括号),这来自 uniform access principle。总感觉 scala 类似的约定太多,这又不是强制性的,新手很容易弄错。正因为如此,如果 abstract class 里面要求提供某个 def,子类甚至可以用个 val 对应的变量实现而不需要重写这个方法。

构造函数

scala 的构造函数设计比较诡异,它专门在 class 后面添加了括号提供一个主要的构造函数,当然可以在 class {} 里面通过 this 定义别的构造函数,但从这个设计来看鼓励使用一些简单的类,仅用一个构造函数,其他的作为辅助使用。同时继承的父类的构造函数可以在 extends 父类后面的括号里面写出来。

equals 与 eq

scala 为了区分值等与引用等,对 AnyVal 处理时 equals、== 之类的对值比较,而 AnyRef 则是对引用比较,因为可以用 java 的代码,也提供了 eq 来做 ref 比较(比如 boxed primitives)。scala 的 AnyVal 一般都是 abstract class,因此无法 new,他们可以隐式转换到 scala.runtime.Rich* 类,这样就能为这些类型提供更多的功能(方法)。

package

其实 scala 的 package 可以跟 namespace 一样嵌套来写,只是对应的仍然是 classpath 的路径,但是 scala 也允许使用 java 的那种形式来声明,同时后面可以继续跟 package 表示嵌套在内层的 package。同时也支持 import 时候的 alias,这点跟 python 的 import as 有点像,与 Java 不同的是 java 只能 import class/interface,而这里可以 import package。这里的 alias 通过 => 表达。如 import java.util.{regex => RE}。默认情况下 import 了 java.lang,scala 和 Predef 里面的东西。通过 private[package-name/this] 可以控制一些成员的访问。这些访问和伴随的 object 是共享的,这点可以直接将 object 当作 static 方法来看。

——————
And thy seed shall be as the dust of the earth, and thou shalt spread abroad to the west, and to the east, and to the north, and to the south: and in thee and in thy seed shall all the families of the earth be blessed.

Advertisements
scala 笔记(4)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s