除了从父类集成代码外,Scala中的类还允许从一个或者多个traits中导入代码。
对于Java程序员来说理解traits的最好方法就是把他们当作可以包含代码的接口(interface)。在Scala中,当一个类继承一个trait时,它就实现了这个trait的接口,同时还从这个trait中继承了所有的代码。
让我们通过一个典型的实例来看看这种trait机制是如何发挥作用的:排序对象。能够比较若干给定类型的对象在实际应用中是很有用的,比如在进行排序时。在Java语言中可以比较的对象是通过实现Comparable接口完成的 。在Scala中我们可以通过吧Comparable定义为trait来做的比Java好一些。我们吧这个trait叫做Ord。
在比较对象时,一下六种关系通常使用率最高:小于、小于等于、等于、不等于、大于等于、大于。但是把他们都定义一次无疑是很没用而且繁琐的。尤其是六种关系中的四种其实是可以通过其他两种关系导出的。例如给定等于和小于的定义后就可以推导出其他的定义。于是在Scala��,这些推导可以通过下面这个trait实现:
trait Ord{
def<(that:Any):Boolean
def<=(that:Any):Boolean=(this< that) (this== that)
def>(that:Any):Boolean=!(this<= that)
def>=(that:Any):Boolean=!(this< that)
}
这个定义在建立了一个叫做与Java中的 Comparable 等效的叫做 Ord的类型的同时还实现了使用抽象的一种关系推导其他三种的接口。比较相等性的方法没有出现是由于他已经默认存在于所有对象中了。
上面使用的叫做Any的类型表示了Scala中所有类的共同超类。事实上它就等于Java语言中的Object。
要使的一个类可以被比较,就需要可以比较他们是否相等或者大小关系,而这些都混合在上面的类Ord中了。现在我们来写一个Date类来表示格利高里历中的日期。这个日期由年、月、日三个部分组成,每个部分都可以用一个整数表示。所有我们就得出了下面这个定义:
classDate(y:Int, m:Int, d:Int)extendsOrd{
def year = y
def month = m
def day = d
overridedef toString():String= year +"-"+ month +"-"+ day
}
注意在类名后出现的extends Ord。这表示了这个类继承了Ord这个trait。
然后我们重新定义了equals这个从Object继承来的方法,好让他能够正确的比较我们日期中的每个部分。原来的equals函数的行为与Java中的一样,是按照对象的指针进行比较的。我们可以得出下面的代码。
overridedef equals(that:Any):Boolean=
that.isInstanceOf[Date]&&{
val o = that.asInstanceOf[Date]
o.day == day && o.month == month && o.year == year
}
这个函数使用了预定义函数 isInstanceOf 和asInstanceOf 。第一个isInstanceOf 类似Java中的 instanceof :当且仅当对象是给定类型的实例时才返回true。第二个 asInstanceOf 对应Java中的类型转换操作:当对象是给定类型的子类时转换,否则抛出ClassCastException。
最后我们还需要定义测试小于关系的函数,如下面所示。这个函数使用了预定义���函数error ,它可以使用给定字符串抛出一个异常。
def<(that:Any):Boolean={
if(!that.isInstanceOf[Date])
error("cannot compare "+ that +" and a Date")
val o = that.asInstanceOf[Date](year < o.year)
(year == o.year &&(month < o.month
(month == o.month && day < o.day)))
}
以上就是Data的完整定义了。这个类的实例既可以作为日期显示,也可以进行比较。而且他们都定义了6种比较操作:其中两种 : equals和< 是我们直接定义的,而其他的是从Ord中继承的。