trait类似于java中的interface,但是有所不同

  • Scala中的trait是一种特殊的概念;
  • 首先先将trait作为接口使用,此时的trait就与Java中的接口 (interface)非常类似;
  • 在trait中可以定义抽象方法,就像抽象类中的抽象方法一样,只要不给出方法的方法体即可;
  • 类可以使用extends关键字继承trait,注意,这里不是 implement,而是extends ,在Scala中没有 implement 的概念,无论继承类还是trait,统一都是 extends;
  • 类继承后,必须实现其中的抽象方法,实现时,不需要使用 override 关键字;
  • Scala不支持对类进行多继承,但是支持多重继承 trait,使用 with 关键字即可。
package com.zy.scala.trait_demo

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
}
}

在trait中定义具体的方法

Scala中的trait不仅可以定义抽象方法,还可以定义具体的方法,此时 trait 更像是包含了通用方法的工具,可以认为trait还包含了类的功能

package com.zy.scala.trait_demo

/**
* 比如 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]
}
}

在trait中定义具体field

  • Scala 中的 trait 可以定义具体的 field,此时继承 trait 的子类就自动获得了 trait 中定义的 field;
  • 但是这种获取 field 的方式与继承 class 的是不同的。 如果是继承 class 获取的 field ,实际上还是定义在父类中的;而继承 trait获取的 field,就直接被添加到子类中了。
package com.zy.scala.trait_demo

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
}
}

在trait中定义抽象field

  • Scala中的trait也能定义抽象field, 而trait中的具体方法也能基于抽象field编写;
  • 继承trait的类,则必须覆盖抽象field,提供具体的值;
package com.zy.scala.trait_demo

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)
}
}

在实例对象指定混入某个trait

  • 可在创建类的对象时,为该对象指定混入某个trait,且只有混入了trait的对象才具有trait中的方法,而其他该类的对象则没有;
  • 在创建对象时,使用 with 关键字指定混入某个 trait;
package com.zy.scala.trait_demo

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!
}
}

trait 调用链

  • Scala中支持让类继承多个trait后,可依次调用多个trait中的同一个方法,只要让多个trait中的同一个方法,在最后都依次执行 super 关键字即可;
  • 类中调用多个trait中都有的这个方法时,首先会从最右边的trait的方法开始执行,然后依次往左执行,形成一个调用链条;
  • 这种特性非常强大,其实就是设计模式中责任链模式的一种具体实现;
package com.zy.scala.trait_demo

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
}
}

混合使用 trait 的具体方法和抽象方法

  • 在 trait 中,可以混合使用具体方法和抽象方法;
  • 可以让具体方法依赖于抽象方法,而抽象方法则可放到继承 trait的子类中去实现;
  • 这种 trait 特性,其实就是设计模式中的模板设计模式的体现;
package com.zy.scala.trait_demo

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)
}
}

trait的构造机制

  • 在Scala中,trait也是有构造代码的,即在trait中,不包含在任何方法中的代码;
  • 继承了trait的子类,其构造机制如下:
  • 父类的构造函数先执行, class 类必须放在最左边;多个trait从左向右依次执行;构造trait时,先构造父 trait,如果多个trait继承同一个父trait,则父trait只会构造一次;所有trait构造完毕之后,子类的构造函数最后执行。
package com.zy.scala.trait_demo

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!
}
}

trait 继承 class

package com.zy.scala.trait_demo

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基础:面向对象之trait的更多相关文章

  1. 第2节 Scala中面向对象编程:12、13、14、15、16、trait

    6.4.  Scala中面向对象编程之trait 6.4.1.    将trait作为接口使用 Scala中的trait是一种特殊的概念: 首先先将trait作为接口使用,此时的trait就与Java ...

  2. 【Scala学习之一】 Scala基础语法

    环境 虚拟机:VMware 10 Linux版本:CentOS-6.5-x86_64 客户端:Xshell4 FTP:Xftp4 jdk1.8 scala-2.10.4(依赖jdk1.8) spark ...

  3. 1.scala基础语法总结

    Scala基础语法总结:Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的.如果一行里写多个语句那么分号是需要的 val s = "菜鸟教程"; pr ...

  4. Scala基础简述

    * Scala基础简述 本文章作为Scala快速学习的教程,前提环境是:我假设在此之前,你已经学会了Java编程语言,并且我们以随学随用为目标(在此不会深度挖掘探讨Scala更高级层次的知识).其中语 ...

  5. Scala之面向对象

    1. Scala基础练习 不使用str.toLong,str.toInt/Integer.valueOf()/Long.valueOf/Integer.parseInt()等,将字符串"12 ...

  6. Scala学习笔记--特质trait

    http://outofmemory.cn/scala/scala-trait-introduce-and-example 与Java相似之处 Scala类型系统的基础部分是与Java非常相像的.Sc ...

  7. spark快速开发之scala基础之1 数据类型与容器

    写在前面 面向java开发者.实际上,具有java基础学习scala是很容易.java也可以开发spark,并不比scala开发的spark程序慢.但学习scala可有助于更快更好的理解spark.比 ...

  8. 大数据技术之_16_Scala学习_04_函数式编程-基础+面向对象编程-基础

    第五章 函数式编程-基础5.1 函数式编程内容说明5.1.1 函数式编程内容5.1.2 函数式编程授课顺序5.2 函数式编程介绍5.2.1 几个概念的说明5.2.2 方法.函数.函数式编程和面向对象编 ...

  9. Scala基础知识[一]

    摘要:在Scala 是 Scalable Language 的简写,是一门多范式(multi-paradigm)的编程语言.设计初衷是要集成面向对象编程和函数式编程的各种特性.Scala 运行在Jav ...

  10. SCALA基础知识学习

    注:本文只说和Java不同的地方. 总结自: Scala详细教程 Scala教程 scala基础语法 Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ";" 是 ...

随机推荐

  1. python之wheel 包命名规则、abi 兼容和安装

    一.windows安装python包,遇见的问题 1.python3以后的版本,安装python包,可以直接使用pip安装,但是安装时偶尔报错 2.安装python源码包,如何确定自己该安装哪个版本, ...

  2. spring--集合注入(常规方法)

    数据,list,set,map,Properties 集合注入 package Spring_collections; /** * Created by luozhitao on 2017/8/11. ...

  3. sysbench fileio 压力测试

    备注:   使用的是yum 安装   1. 安装 yum install -y sysbench 2. 命令 fileio options: --file-num=N number of files ...

  4. 表格头部与左侧内容随滚动条位置改变而改变(基于jQuery)

    效果图如下: HTML代码: <!DOCTYPE html> <html lang="zh-CN"> <head> <meta chars ...

  5. 亚马逊MWS开发套路演示

    MWS是商城网络服务的缩写,具体介绍看这里http://docs.developer.amazonservices.com/zh_CN/dev_guide/DG_IfNew.html.MWS就是一组A ...

  6. adb error: device offline

    adb 调试一直报错 $ adb shell error: device offline 解决办法: $ adb kill-server $ adb start-server * daemon not ...

  7. Redis事务及锁应用

    Redis只支持简单的事务,不像mysql那样比较完整严格,对数据的完整性也维持的很好.redis的开启事务实际上只是将开启事务之后的一段命令用队列包裹起来了,当调用redis的执行命令(exec)全 ...

  8. Java字符代码中干掉制表符、回车符和换行符

    Java字符代码中干掉制表符.回车符和换行符 代码片段: String sql = StringUtils.trim(sql).replaceAll("[\\r\\n\\t]",& ...

  9. MySql——触发器

    触发器 什么叫触发器: 就是mysql中的一种“一触即发”的机器(机制). 其实只是预先定义好的一段代码.该段代码无需人工调用,而是会在‘预计’好的某个情形下自动执行. 通常就这几个情形: 对某个数据 ...

  10. JAVA for循环语句的循环变量类型问题

    class HalfDollars { public static void main(String [] arguments) { int[] denver = {1_900_000,1_700_0 ...