文章目录
scala当中的特质trait
1、将trait作为接口使用
Scala中的trait是一种特殊的概念;
首先先将trait作为接口使用,此时的trait就与Java中的接口 (interface)非常类似;
在trait中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
类可以使用extends关键字继承trait,注意,这里不是 implement,而是extends ,在Scala中没有 implement 的概念,无论继承类还是trait,统一都是 extends;
类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
Scala不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可。
package cn.itcast.triat
trait HelloTrait {
def sayHello(): Unit
}
trait MakeFriendsTrait {
def makeFriends(c: Children): Unit
}
//多重继承 trait
class Children(val name: String) extends HelloTrait with MakeFriendsTrait with Cloneable with Serializable{
def sayHello() =println("Hello, " + this.name)
def makeFriends(c: Children) = println("Hello, my name is " + this.name + ", your name is " + c.name)
}
object Children{
def main(args: Array[String]) {
val c1=new Children("tom")
val c2=new Children("jim")
c1.sayHello()//Hello, tom
c1.makeFriends(c2)//Hello, my name is tom, your name is jim
}
}
2、在trait中定义具体的方法
Scala中的trait不仅可以定义抽象方法,还可以定义具体的方法,此时 trait 更像是包含了通用方法的工具,可以认为trait还包含了类的功能。
/**
* 比如 trait 中可以包含很多子类都通用的方法,例如打印日志或其他工具方法等等。
* spark就使用trait定义了通用的日志打印方法;
*/
trait Logger {
def log(message: String): Unit = println(message)
}
class PersonForLog(val name: String) extends Logger {
def makeFriends(other: PersonForLog) = {
println("Hello, " + other.name + "! My name is " + this.name + ", I miss you!!")
this.log("makeFriends method is invoked with parameter PersonForLog[name = " + other.name + "]")
}
}
object PersonForLog{
def main(args: Array[String]) {
val p1=new PersonForLog("jack")
val p2=new PersonForLog("rose")
p1.makeFriends(p2)
//Hello, rose! My name is jack, I miss you!!
//makeFriens method is invoked with parameter PersonForLog[name = rose]
}
}
3、在trait中定义具体field
Scala 中的 trait 可以定义具体的 field,此时继承 trait 的子类就自动获得了 trait 中定义的 field;
但是这种获取 field 的方式与继承 class 的是不同的。 如果是继承 class 获取的 field ,实际上还是定义在父类中的;而继承 trait获取的 field,就直接被添加到子类中了。
trait PersonForField {
val age:Int=50
}
//继承 trait 获取的field直接被添加到子类中
class StudentForField(val name: String) extends PersonForField {
def sayHello = println("Hi, I'm " + this.name + ", my age is "+ age)
}
object StudentForField{
def main(args: Array[String]) {
val s=new StudentForField("tom")
s.sayHello
}
}
4、在trait中定义抽象field
Scala中的trait也能定义抽象field, 而trait中的具体方法也能基于抽象field编写;
继承trait的类,则必须覆盖抽象field,提供具体的值;
trait SayHelloTrait {
val msg:String
def sayHello(name: String) = println(msg + ", " + name)
}
class PersonForAbstractField(val name: String) extends SayHelloTrait {
//必须覆盖抽象 field
val msg = "Hello"
def makeFriends(other: PersonForAbstractField) = {
this.sayHello(other.name)
println("I'm " + this.name + ", I want to make friends with you!!")
}
}
object PersonForAbstractField{
def main(args: Array[String]) {
val p1=new PersonForAbstractField("Tom")
val p2=new PersonForAbstractField("Rose")
p1.makeFriends(p2)
}
}
5、在实例对象指定混入某个trait
可在创建类的对象时,为该对象指定混入某个trait,且只有混入了trait的对象才具有trait中的方法,而其他该类的对象则没有;
在创建对象时,使用 with 关键字指定混入某个 trait;
trait LoggedTrait {
// 该方法为实现的具体方法
def log(msg: String) = {}
}
trait MyLogger extends LoggedTrait{
// 覆盖 log() 方法
override def log(msg: String) = println("log: " + msg)
}
class PersonForMixTraitMethod(val name: String) extends LoggedTrait {
def sayHello = {
println("Hi, I'm " + this.name)
log("sayHello method is invoked!")
}
}
object PersonForMixTraitMethod{
def main(args: Array[String]) {
val tom= new PersonForMixTraitMethod("Tom").sayHello //结果为:Hi, I'm Tom
// 使用 with 关键字,指定混入MyLogger trait
val rose = new PersonForMixTraitMethod("Rose") with MyLogger
rose.sayHello
// 结果为: Hi, I'm Rose
// 结果为: log: sayHello method is invoked!
}
}
6、trait 调用链
Scala中支持让类继承多个trait后,可依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法,在最后都依次执行 super 关键字即可;
类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条;
这种特性非常强大,其实就是设计模式中责任链模式的一种具体实现;
trait HandlerTrait {
def handle(data: String) = {println("last one")}
}
trait DataValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check data: " + data)
super.handle(data)
}
}
trait SignatureValidHandlerTrait extends HandlerTrait {
override def handle(data: String) = {
println("check signature: " + data)
super.handle(data)
}
}
class PersonForRespLine(val name: String) extends SignatureValidHandlerTrait with DataValidHandlerTrait {
def sayHello = {
println("Hello, " + this.name)
this.handle(this.name)
}
}
object PersonForRespLine{
def main(args: Array[String]) {
val p=new PersonForRespLine("tom")
p.sayHello
//执行结果:
// Hello, tom
// check data: tom
// check signature: tom
// last one
}
}
7、混合使用 trait 的具体方法和抽象方法
在 trait 中,可以混合使用具体方法和抽象方法;
可以让具体方法依赖于抽象方法,而抽象方法则可放到继承 trait的子类中去实现;
这种 trait 特性,其实就是设计模式中的模板设计模式的体现;
trait ValidTrait {
//抽象方法
def getName: String
//具体方法,具体方法的返回值依赖于抽象方法
def valid: Boolean = {"Tom".equals(this.getName)
}
}
class PersonForValid(val name: String) extends ValidTrait {
def getName: String = this.name
}
object PersonForValid{
def main(args: Array[String]): Unit = {
val person = new PersonForValid("Rose")
println(person.valid)
}
}
8、trait的构造机制
在Scala中,trait也是有构造代码的,即在trait中,不包含在任何方法中的代码;
继承了trait的子类,其构造机制如下:
父类的构造函数先执行, class 类必须放在最左边;多个trait从左向右依次执行;构造trait时,先构造父 trait,如果多个trait继承同一个父trait,则父trait只会构造一次;所有trait构造完毕之后,子类的构造函数最后执行。
class Person_One {
println("Person's constructor!")
}
trait Logger_One {
println("Logger's constructor!")
}
trait MyLogger_One extends Logger_One {
println("MyLogger's constructor!")
}
trait TimeLogger_One extends Logger_One {
println("TimeLogger's contructor!")
}
class Student_One extends Person_One with MyLogger_One with TimeLogger_One {
println("Student's constructor!")
}
object exe_one {
def main(args: Array[String]): Unit = {
val student = new Student_One
//执行结果为:
// Person's constructor!
// Logger's constructor!
// MyLogger's constructor!
// TimeLogger's contructor!
// Student's constructor!
}
}
9、trait 继承 class
在Scala中trait 也可以继承 class,此时这个 class 就会成为所有继承该 trait 的子类的超级父类。
class MyUtil {
def printMsg(msg: String) = println(msg)
}
trait Logger_Two extends MyUtil {
def log(msg: String) = this.printMsg("log: " + msg)
}
class Person_Three(val name: String) extends Logger_Two {
def sayHello {
this.log("Hi, I'm " + this.name)
this.printMsg("Hello, I'm " + this.name)
}
}
object Person_Three{
def main(args: Array[String]) {
val p=new Person_Three("Tom")
p.sayHello
//执行结果:
// log: Hi, I'm Tom
// Hello, I'm Tom
}
}
模式匹配和样例类
Scala有一个十分强大的模式匹配机制,可以应用到很多场合:如switch语句、类型检查等。并且Scala还提供了样例类,对模式匹配进行了优化,可以快速进行匹配。
1、字符匹配
def main(args: Array[String]): Unit = {
val charStr = '6'
charStr match {
case '+' => println("匹配上了加号")
case '-' => println("匹配上了减号")
case '*' => println("匹配上了乘号")
case '/' => println("匹配上了除号")
//注意。所有的模式匹配都必须最终匹配上一个值,如果没有匹配上任何值,就会报错
// case _ => println("都没有匹配上,我是默认值")
}
}
2、 匹配字符串
def main(args: Array[String]): Unit = {
val arr = Array("hadoop", "zookeeper", "spark")
val name = arr(Random.nextInt(arr.length))
name match {
case "hadoop" => println("大数据分布式存储和计算框架...")
case "zookeeper" => println("大数据分布式协调服务框架...")
case "spark" => println("大数据分布式内存计算框架...")
case _ => println("我不认识你...")
}
}
3、守卫
模式匹配当中,我们也可以通过条件进行判断
def main(args: Array[String]): Unit = {
var ch = "500"
var sign = 0
ch match {
case "+" => sign = 1
case "-" => sign = 2
case _ if ch.equals("500") => sign = 3
case _ => sign = 4
}
println(ch + " " + sign)
}
4、 匹配类型
注意在map当中会存在泛型擦除的情况。注意在进行非数组的类型匹配的时候,类型都会进行擦除
def main(args: Array[String]): Unit = {
//注意泛型擦除,在模式匹配当中的类型匹配中,除了Array类型以为,所有的其他的数据类型都会被擦除掉
val a = 3
val obj = if(a == 1) 1
else if(a == 2) "2"
else if(a == 3) BigInt(3)
else if(a == 4) Map("aa" -> 1)
else if(a == 5) Map(1 -> "aa")
else if(a == 6) Array(1, 2, 3)
else if(a == 7) Array("aa", 1)
else if(a == 8) Array("aa")
val r1 = obj match {
case x: Int => x
case s: String => s.toInt
case BigInt => -1 //不能这么匹配
case _: BigInt => Int.MaxValue
case m: Map[String, Int] => "Map[String, Int]类型的Map集合"
case m: Map[_, _] => "Map集合"
case a: Array[Int] => "It's an Array[Int]"
case a: Array[String] => "It's an Array[String]"
case a: Array[_] => "It's an array of something other than Int"
case _ => 0
}
println(r1 + ", " + r1.getClass.getName)
}
5、匹配数组、元组、集合
def main(args: Array[String]): Unit = {
val arr = Array(0, 3, 5)
arr match {
case Array(0, x, y) => println(x + " " + y)
case Array(0) => println("only 0")
//匹配数组以1 开始作为第一个元素
case Array(1, _*) => println("0 ...")
case _ => println("something else")
}
val lst = List(3, -1)
lst match {
case 0 :: Nil => println("only 0")
case x :: y :: Nil => println(s"x: $x y: $y")
case 0 :: tail => println("0 ...")
case _ => println("something else")
}
val tup = (1, 3, 7)
tup match {
case (1, x, y) => println(s"1, $x , $y")
case (_, z, 5) => println(z)
case _ => println("else")
}
}
注意:在Scala中列表要么为空(Nil表示空列表)要么是一个head元素加上一个tail列表。
9 :: List(5, 2) :: 操作符是将给定的头和尾创建一个新的列表
注意::: 操作符是右结合的,如9 :: 5 :: 2 :: Nil相当于 9 :: (5 :: (2 :: Nil))
6、 样例类
样例类首先是类,除此之外它是为模式匹配而优化的类,样例类用case关键字进行声明。样例类主要是使用在我们后面的sparkSQL当中,通过样例类来映射我们的表当中的对象
定义形式:
case class 类型,是多例的,后面要跟构造参数。 case class Student(name:String)
case object 类型,是单例的。 case object Person
case class SubmitTask(id: String, name: String)
case class HeartBeat(time: Long)
case object CheckTimeOutTask
//1、样例类当中的主构造器参数默认为val的
//2、样例类当中的apply和unapply方法自动生成
object CaseDemo04 extends App {
val arr = Array(CheckTimeOutTask, HeartBeat(12333), SubmitTask("0001", "task-0001"))
arr(2) match {
case SubmitTask(id, name) => {
println(s"$id,$name")
println(id)
println(name)
println(id+"\t"+name)
}
case HeartBeat(time) => {
println(time)
}
case CheckTimeOutTask => {
println("check")
}
}
}
7、偏函数
被包在花括号内没有match的一组case语句是一个偏函数,它是PartialFunction[A, B]的一个实例,A代表输入参数类型,B代表返回结果类型,常用作输入模式匹配,偏函数最大的特点就是它只接受和处理其参数定义域的一个子集。
val func1: PartialFunction[String, Int] = {
case "one" => 1
case "two" => 2
// case _ => -1
}
def func2(num: String) : Int = num match {
case "one" => 1
case "two" => 2
case _ => -1
}
def main(args: Array[String]) {
println(func1("one"))
println(func2("one"))
//如果偏函数当中没有匹配上,那么就会报错,我们可以通过isDefinedAt来进行判断
// println(func1("three"))
println(func1.isDefinedAt("three"))
if(func1.isDefinedAt("three")){
println("hello world")
}else{
println("world hello")
}
}