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

[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 节中给出。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值