使用布局例子演示组合与继承,可以使用如下方法创建一个元素:

1
elem(s: String): Element

并且 Element 上可以进行 above 和 beside 操作:

1
2
3
4
5
6
7
val column1 = elem("hello") above elem("***")
val column2 = elem("***") above elem("world")
column1 beside column2

// 打印结果如下
hello ***
*** world

抽象类

我们可以创建一个抽象元素类:

1
2
3
abstract class Element {
def contents: Array[String]
}

抽象类不能被实例化,contents 方法没有带 abstract 修饰符,但也是抽象方法,因为如果方法没有实现,它就是抽象的。

定义无参数方法

我们可以在抽象类中定义一些实现的方法:

1
2
3
4
5
abstract class Element {
def contents: Array[String]
def height: Int = contents.length
def width: Int = if (height == 0) 0 else contents(0).length
}

在 Element 当中的三个方法都没有参数列表,甚至连个空列表都没有,这种称为无参数方法。

1
def width(): Int //属于空列表,称为空括号方法。

推荐使用无参数方法,无参数方法的含义是可以把 width 和 height 作为字段:

1
2
3
4
5
6
abstract class Element {
def contents: Array[String]
val height = contents.length
val width =
if (height == 0) 0 else contents(0).length
}

总结起来,Scala 里定义不带参数也没有副作用的方法为无参数方法,也就是说,省略空的括号, 是鼓励的风格。另一方面,永远不要定义没有括号的带副作用的方法,因为那样的话方法调用看上去会像选择一个字段。这样你的客户看到了副作用会很奇怪。相同地,当你调用带副作用的函数,请确信写这个调用的时候包括了空的括号。另一种考虑这个问题的方式是,如果你调用的函数执行了操作,使用括号,但如果仅提供了对某个属性的访问,省略括号。

扩展类

继承 Element 创建它的子类:

1
2
3
class ArrayElement(conts: Array[String]) extends Element {
def contents: Array[String] = conts
}

类 ArrayElement 从类 Element 继承所有非私有的成员。在 scala 当中类扩展自 scala.AnyRef,类似于 java 当中的 Object 类。

继承不能继承父类的私有成员。

我们可以如下使用新创建的子类:

1
2
3
4
val ae = new ArrayElement(Array("hello", "world"))
scala> ae.width
res1: Int = 5
val e: Element = new ArrayElement(Array("hello"))

重载方法和字段

在 scala 当中字段和方法属于相同的命名空间,因此字段重载无参数方法称为可能。比如 ArrayElement 中 contents 的实现可以从一个方法变成一个字段。

1
2
3
class ArrayElement(conts: Array[String]) extends Element {
val contents: Array[String] = conts
}

scala 里禁止在同一个类中用同样的名称定义字段和方法。

scala 仅为定义准备了两个命名空间,而 java 有四个。java 的四个命名空间是字段、方法、类型和包。scala 的两个命名空间是值(字段、方法、包、单例对象)和类型

定义参数化字段

在上一个例子的 ArrayElement 类的定义中,它有一个参数 conts,唯一作用是被复制到 contents 字段,选择 conts 参数名只是为了看上去不与 contents 冲突,其实我们可以使用单一的参数化字段,解决这个问题:

1
2
class ArrayElement(val contents: Array[String]) extends Element {
}

现在类 ArrayElement 拥有一个可以从类外部访问的字段 contents,但是不能被重新赋值。我们也可以在参数化字段上添加修饰符,private、protected、override 等。

1
2
3
4
5
class Cat {
val dangerous = false
}
class Tiger(override val dangerous: Boolean, private var age: Int) extents Cat {
}

调用超类构造器

看下面的例子,我们从 ArrayElement 类扩展出来一个 LineElement 类:

1
2
3
4
class LineElement(s: String) extends ArrayElement(Array(s)) {
override def width = s.length
override def height = 1
}

LineElement 扩展了 ArrayElement,并且 ArrayElement 的构造器带一个参数 Array[String],ArrayElement 需要一个参数 String,在 ArrayElement 当中就需要传递一个参数到它的超类的构造器,就可以使用上面的写法。

使用 override 修饰符

在 scala 当中所有重载了父类具体成员的成员都需要这样的修饰符。如果成员实现的 是同名的抽象成员则这个修饰符是可选的。而如果成员并􏰀重载或实现什么其它基类里的成员则 禁用这个修饰符。

定义 final 成员

与 java 一样,如果想定义成不能被子类继承的方法,可以通过添加 final 修饰符。

1
2
3
4
5
class ArrayElement extends Element {
final override def demo() {
println("ArrayElement's implementation invoked")
}
}

【参考资料】

  1. Scala编程

—EOF—