欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

第十二章 Traits - 12.1 trait 怎样工作

程序员文章站 2022-07-04 10:21:00
...
[b][quote]自娱自乐的东西,欢迎拍砖[/quote][/b]

traits 是Scala中能够被重用的一种基本单元。 trait 中封装了方法和字段定义,这样就可以将他们混合成类。和类的继承不同的是,类必须是单继承的,但是一个类中可以混有多个 trait. 这一章就是为你展现 trait 是如何工作的,并且展现了两个最常见的有用的方式:扩宽瘦接口到胖接口(widening thin interfaces to rich ones),并且定义了可存储的更改。并且展现了怎样去运用有序的 trait, 比较了trait 和其他多继承的语言。
trait 怎样工作
一个 trait 的定义和一个类的定义看上去是一样的除了 trait 使用了 trait 关键字。Listing 12.1 展示了一个例子:
 trait Philosophical {
def philosophize() {
println("I consume memory, therefore I am!")
}
}

这个 trait 被命名为 Philosophical. 它并没有声明一个超类,所以它看起来像一个类,它的默认超类是 AnyRef. 它定义了一个方法,叫 philosophize, 是构造方法。它是一个简单的 trait, 仅仅是用来显示 trait 是怎样工作的。
一旦一个 trait 被定义,它可以运用 extends 关键字组合入一个类中。Scala 的程序员更喜欢用 “组合”而不是继承,因为组合一个 trait 和多继承有着很大的不同。这个将在 12.6 节中讨论。例如,Listing 12.2 展现了一个类运用 extends 关键字组合 Philosophical trait:
 class Frog extends Philosophical {
override def toString = "green"
}

你可以运用 extends 关键字组合一个 trait; 这样的话你可以隐式的继承那个 trait 的超类。例如,Listing 12.2 中,类 Frog 是 AnyRef( Philosophical 的超类)的子类并且组合了 Philosophical. 方法继承自一个 trait 的可以像继承自一个超类那样使用,这里是一个例子:
[quote]scala> val frog = new Frog
frog: Frog = green
scala> frog.philosophize()
I consume memory, therefore I am![/quote]
一个 trait 同样定义一个类型。这里是一个 Philosophical 被当成类型用的例子:
[quote]scala> val phil: Philosophical = frog
phil: Philosophical = green
scala> phil.philosophize()
I consume memory, therefore I am![/quote]
phil 的类型是 philosophical, 一个 trait. 这样,变来那个 phil 可以被初始化成任意一个组合了 Philosophical 的对象。
如果你希望组合一个显示继承超类的 trait, 你使用 extends 去指明超类和想要组合的 trait. Listing 12.3 显示了这个例子。如果你想组合多个 trait, 多加些句子。例如,有一个 HasLegs 的 trait, 你可以像 Listing 12.4 中所示的那样将 Philosophical 和 HasLegs 一起组合。
class Animal
class Frog extends Animal with Philosophical {
override def toString = "green"
}


class Animal
trait HasLegs
class Frog extends Animal with Philosophical with HasLegs {
override def toString = "green"
}

在你目前所见到的例子中,Frog 类已经从 Philosophical 继承了一个 philosophize 的实现。或者,Frog 可以重载 Philosophize. 这个语法看上去和重载一个超类中的方法一样。这里是例子:
class Animal
class Frog extends Animal with Philosophical {
override def toString = "green"
override def philosophize() {
println("It ain't easy being "+ toString +"!")
}
}

因为这个 Frog 的定义依然组合了 trait Philosophize, 你可以依然从一个类型的便两种运用它。但是因为 Frog 重载了 Philosophical 的实现,当你调用它的时候你将得到一个新的行为:
scala> val phrog: Philosophical = new Frog
phrog: Philosophical = green
scala> phrog.philosophize()
It ain't easy being green!

在这一点上 trait 有点像有着构造方法的 Java 里面的接口,但是他们能做到的事情更多。比如 Trait 可以声明一个语句并且维护它。实际上,你可以做任何在类中可做的事情,并且语法看起来都是一样的,仅仅有两点不同。首先,一个 Trait 不能有任何一个类类型的参数,也就是说,它的构造函数中不能有类类型的参数传递给它。换一种说法,虽然你能够将类定义成这样:
class Point(x: Int, y: Int)

但是下面相同形式的定义 Trait 却不能正确的编译。
trait NoPoint(x: Int, y: Int) // Does not compile

你将在 25 章中学习如何去绕过这种限制。
类和 Trait 的另一个不同在于,类的超类是静态绑定的,在 Trait 中是动态绑定的。如果你在类中写 “super.toString”, 你能够准确的知道哪一个方法实现将会被调用。当你在 Trait 中写相同的东西时,当你定义 Trait 时,将要被调用的方法实现的超类还没有被定义。甚至于说,当每次那个 Trait 在一个类的构造中混入时将要每次都再次决定。这个超类的哦你动作是允许 Trait 工作的可堆栈改变的关键,这将在 12.5 节中详述。超类调用的规则的解决办法将在 12.6 节中给出。
相关标签: 工作 Scala

上一篇: LINQ语句

下一篇: openGL之材质