作为接口使用的特质

Scala中的trait特质是一种特殊的概念。

首先可以将trait作为接口来使用,此时的trait就与Java中的接口非常类似。

在trait中可以定义抽象方法,与抽象类中的抽象方法一样,只要不给出方法的具体实现即可。

类可以使用extends关键字继承trait。

注意:在Scala中没有implement的概念,无论继承类还是trait特质,统一都是extends。

类继承trait特质后,必须实现其中的抽象方法,实现时可以省略override关键字。

Scala不支持对类进行多继承,但是支持多重继承trait特质,使用with关键字即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package lagou.cn.part06

trait HelloTrait{
def sayHello
}

trait MakeFriendTrait{
def makFriend
}
//如果一个类继承了多个Trait特质,第一个Trait用extends,其他Trait用with关键字
class Person(name:String) extends HelloTrait with MakeFriendTrait{
override def sayHello: Unit = {
println(s"Hello, My name is $name")
}

override def makFriend: Unit = {
println(s"Hello,$name")
}
}

object TraitDemo {
def main(args: Array[String]): Unit = {
val person=new Person("jacky")
person.sayHello
person.makFriend
}
}

带有具体实现的特质

  • 具体方法

    Scala中的trait特质不仅仅可以定义抽象方法,还可以定义具体实现的方法,这时的trait更像是包含了通用工具方法的类。比如,trait中可以包含一些很多类都通用的功能方法,比如打印日志等等,Spark中就使用了trait来定义通用的日志打印方法。

  • 具体字段

    Scala trait特质中的字段可以是抽象的,也可以是具体的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package lagou.cn.part06

trait People {
val name: String
val age = 30

def eat: Unit = {
println("Eating......")
}
}

trait Worker {
val age = 25

def work: Unit = {
println("Working......")
}
}

class Student extends Worker with People {
//重写name抽象字段,此处override可以省略
override val name: String = "lisi"
//由于Worker和People中都有age字段,所以当Student类继承这两个特质时,需要重写age字段
//并且要使用override关键字,否则就会报错。
//此时的override关键字不能省略。
override val age = 20
}

object TraitDemoTwo {
def main(args: Array[String]): Unit = {
val stu = new Student
stu.eat
stu.work
println(s"姓名:${stu.name},年龄:${stu.age}")
}
}

注意:特质Person和Worker中都有age字段,当Student继承这两个特质时,需要重写age字段,并且要用override关键字,否则就会报错。

特质构造顺序

在Scala中,trait特质也是有构造器的,也就是trait中的不包含在任何方法中的代码。

构造器以如下顺序执行:

1、执行父类的构造器;

2、执行trait的构造器,多个trait从左到右依次执行;

3、构造trait时会先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次;

4、所有trait构造完毕之后,子类的构造器才执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package lagou.cn.part06

class Person2 {
println("Person's Constructor!")
}

trait Logger {
println("Logger's Constructor!")
}

trait MyLogger extends Logger {
println("MyLogger's Constructor!")
}

trait TimeLogger extends Logger {
println("TimeLogger's Constructor!")
}

//如果一个类既继承了父类,也继承特质,那么要先写父类,再写特质
//extends 父类 with 特质1 with 特质2.....
class Student2 extends Person2 with MyLogger with TimeLogger {
println("Student's Constructor!")
}

object TraitDemoThree {
def main(args: Array[String]): Unit = {
val stu = new Student2
}
}

执行结果:

1
2
3
4
5
Person's Constructor!
Logger's Constructor!
MyLogger's Constructor!
TimeLogger's Constructor!
Student's Constructor!

特质继承类

在Scala中,trait特质也可以继承class类,此时这个class类就会成为所有继承此trait的类的父类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package lagou.cn.part06

class MyUtil {
def printMessage(msg: String): Unit = {
println(msg)
}
}

trait Log extends MyUtil {
def log(msg: String): Unit = {
println(msg)
}
}
//Person3继承了Log特质,Log特质继承了MyUtil类,那么MyUtil类就成为Person3的父类
class Person3(name: String) extends Log {
def sayHello: Unit = {
log("hello, " + name)
printMessage("hi, " + name)
}
}

object TraitDemoFour {
def main(args: Array[String]): Unit = {
val person = new Person3("jacky")
person.sayHello
}
}

Ordered和Ordering

在Java中对象的比较有两个接口,分别是Comparable和Comparator。它们之间的区别在于:

  • 实现Comparable接口的类,重写compareTo()方法后,其对象自身就具有了可比较性;

  • 实现Comparator接口的类,重写了compare()方法后,则提供一个第三方比较器,用于比较两个对象;

在Scala中也引入了以上两种比较方法(Scala.math包下):

  • Ordered特质混入Java的Comparable接口,它定义了相同类型间的比较方式,但这种内部比较方式是单一的;

    1
    trait Ordered[A] extends Any with java.lang.Comparable[A]{......}
  • Ordering特质混入Comparator接口,它是提供第三方比较器,可以自定义多种比较方式,在实际开发中也是使用比较多的,灵活解耦合。

    1
    trait Ordering[T] extends Comparator[T] with PartialOrdering[T] with Serializable {......}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    package lagou.cn.part06

    import scala.util.Sorting

    case class Project(tag: String, score: Int) extends Ordered[Project] {
    override def compare(that: Project): Int = {
    tag.compareTo(that.tag)
    }
    }

    object OrderDemo {
    def main(args: Array[String]): Unit = {
    val list = List(Project("hadoop", 40), Project("flink", 90), Project("spark", 80), Project("hive", 60))
    println(list.sorted)

    val pairs = Array(("a", 7, 2), ("b", 9, 1), ("c", 8, 3))
    //Ordering.by[(String,Int,Int),Int](_._2)表示从Tuple3转到Int型,根据Tuple3中的第二个元素进行排序
    Sorting.quickSort(pairs)(Ordering.by[(String,Int,Int),Int](_._2))
    println(pairs.toBuffer)
    }
    }