基础知识

1 将trait作为接口使用

此时Trait就与Java中的接口非常类似,不过注意,在Scala中无论继承还是trait,统一都是extends关键字。

Scala跟Java 8前一样不支持对类进行多继承,但是支持多重继承trait,使用with关键字即可


trait HelloTrait{
def sayHello(name: String)
}
trait MakeFriends{
def makeFriends(p: Person)
}
class Person(val name: String) extends HelloTrait with MakeFriends {
def sayHello(name: String) = println("Hello, " + name)
def makeFriends(p: Person) = println("hello " + p.name + ", I'm " + name)
}
defined trait HelloTrait
defined trait MakeFriends
defined class Person
scala> val p = new Person("spark")
p: Person = Person@2f29e630
scala> val p2 = new Person("jack")
p2: Person = Person@52f118aa
scala> p.sayHello("jack")
Hello, jack
scala> p.makeFriends(p2)
hello jack, I'm spark

2 在trait中定义具体方法

Trait不仅可以定以抽象方法,还可以定以具体方法,此时Trait更像是包含了通过工具方法的东西。

有一个专有名词来形容这种情况,叫做Trait功能混入了类

举例:trait中可以包含一些很多类都通用的方法,比如说打印日志等,Spark中就是用了trait来定义了通用的日志打印方法。


trait Logger {
def log(msg: String) = println("log: " + msg)
}
class Person(val name: String) extends Logger {
def sayHello { println("Hello, I'm " + name); log("sayHello is invoked") }
}
defined trait Logger
defined class Person
scala> val p = new Person("leo")
p: Person = Person@3b0c38f2
scala> p.sayHello
Hello, I'm leo
log: sayHello is invoked

3 在trait中定义具体字段

Trait可以定以具体field, 但是这种继承trait field的方式与继承class是原理不同的:如果是继承class获取的field,实际是定以在父类中的;而继承trait获取的field,就直接被添加到了继承类中。 

4 在trait中定义抽象字段

Trait中可以定以抽象field, 而trait中的具体方法可以使用抽象field,但是继承trait的类必须要覆盖抽象field,提供具体的值,否则程序会运行出错。


// trait中的具体方法可以使用抽象field
trait SayHello{
val msg: String
def sayHello(name: String) = println(msg + "," + name)
}
// 继承trait中必须覆盖抽象field
class Person(val name: String) extends SayHello{
val msg: String = "hello"
def makeFriends(p: Person){
sayHello(p.name)
println("I'm" + name + ", want to make friends with you")
}
}
defined trait SayHello
defined class Person
// 测试
scala> val p1 = new Person("leo")
p1: Person = Person@67372d20
scala> val p2 = new Person("Sparks")
p2: Person = Person@4f1f2f84
scala> p1.makeFriends(p2)
hello,Sparks
I'mleo, want to make friends with you

Trait进阶

为实例混入trait

有时我们可以在创建类的对象时,指定该对象混入某个trait,这样就只有这个对象混入该trait的方法,而类的其他对象则没有


trait Logged {
def log(msg: String) {}
}
trait MyLogger extends Logged {
override def log(msg: String) {println("log: " + msg)}
}
class Person (val name: String) extends Logged {
def sayHello { println("Hi, I'm "+ name); log("sayHello is invokend!")}
}
defined trait Logged
defined trait MyLogger
defined class Person
scala> val p1 = new Person("leo")
p1: Person = Person@36f80ceb
scala> p1.sayHello
Hi, I'm leo
// 混入trait,覆盖log方法!
scala> val p2 = new Person("jack") with MyLogger
p2: Person with MyLogger = $anon$1@30a6984c
scala> p2.sayHello
Hi, I'm jack
log: sayHello is invokend!

trait调用链

Scala中支持让类继承多个trait后,依次调用多个trait中的同一个方法(Java中做不到),只要让多个trait的同一个方法中,在最后都执行super.method即可

类中调用多个trait中都有的这个方法时,首先会从最右边的trait方法开始执行,然后依次往左,最终形成一个调用链

这种特性非常强大,其实就相当于设计模式中责任链模式的一种具体实现。


trait Handler{
def handle(data: String) {}
}
trait DataValidHandler extends Handler {
override def handle(data: String){
println("check data:" + data)
// 最后都执行super.method
super.handle(data)
}
}
trait SignatureValidHandler extends Handler {
override def handle(data: String){
println("check signature: " + data)
// 最后都执行super.method
super.handle(data)
}
}
class Person(val name: String) extends SignatureValidHandler with DataValidHandler {
def sayHello = {println("hello, " + name); handle(name)}
}
defined trait Handler
defined trait DataValidHandler
defined trait SignatureValidHandler
defined class Person
scala> val p = new Person("Sparks")
p: Person = Person@4b37d1a4
// 从右往左执行方法
scala> p.sayHello
hello, Sparks
check data:Sparks
check signature: Sparks

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

可以让具体方法依赖于抽象方法,而抽象方法则放到继承trati的类中去实现

这种trait其实就是设计模式中模板设计模式的体现


trait Valid{
// 将getName交给继承类实现,这里直接在具体方法中使用抽象方法
def getName: String
def valid: Boolean = {
getName == "Sparks"
}
}
class Person(val name: String) extends Valid {
println(valid)
def getName = name
}
defined trait Valid
defined class Person
// 测试
scala> val p = new Person("Sparks")
true
p: Person = Person@351fadfa

trait的构造机制

在Scala中,trait也是有构造代码的,也就是trait中除了method中的所有代码 
而继承了trait的类的构造顺序如下:

  1. 父类的构造函数
  2. trait的构造代码,多个trait从左到右依次执行
  3. 构造trait时先构造父trait,如果多个trait继承同一个父trait,则父trait只会构造一次
  4. 所有trait构造完毕后,自身构造函数执行

class Person{ println("Person's constructor!")}
trait Logger { println("Logger's constuctor!")}
trait MyLogger extends Logger { println("MyLogger's constructor!")}
trait TimeLogger extends Logger { println("TimeLogger constructor")}
class Student extends Person with MyLogger with TimeLogger {
println("Student's constructor")
}
defined class Person
defined trait Logger
defined trait MyLogger
defined trait TimeLogger
defined class Student
// 测试构造顺序
scala> val s = new Student
Person's constructor!
Logger's constuctor!
MyLogger's constructor!
TimeLogger constructor
Student's constructor
s: Student = Student@467421cc

trait field初始化

在Scala中,trait是没有接收参数的构造函数的,这是trait与class的唯一区别,但是如果需求就是要trait能够对field进行初始化,那该怎么办呢?

这时候就需要使用Scala中非常特殊的一种高级特性——提前定义

出错示例:


trait SayHello {
val msg: String
println(msg.toString)
}
class Person extends SayHello{
val msg: String = "init"
}
defined trait SayHello
defined class Person
// 因为要首先初始化trait,但是println中使用了抽象field,所以报错
scala> val p = new Person
java.lang.NullPointerException

  
使用提前定义特性初始化trait field


trait SayHello {
val msg: String
println(msg.toString)
}
class Person
defined trait SayHello
defined class Person
// 提前定义
scala> val p = new {
| val msg: String = "init"
| }with Person with SayHello
init
p: Person with SayHello = $anon$1@445c693
// 提前定义另一种写法
scala> class Person extends {
| val msg: String = "init"
| } with SayHello{}
defined class Person
scala> val p = new Person
init
p: Person = Person@121c1a08

  
使用lazy + override初始化trait field


scala> trait SayHello {
| lazy val msg: String = null
| println(msg.toString)
| }
defined trait SayHello
// 覆盖lazy值
scala> class Person extends SayHello {
| override lazy val msg: String = "init"
| }
defined class Person
scala> val p = new Person
init
p: Person = Person@753c7411

trait继承class

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


class MyUtil {
def printMessage(msg: String) = println(msg)
}
trait Logger extends MyUtil{
def log(msg: String) = printMessage("log: " + msg)
}
class Person(val name:String) extends Logger{
def sayHello{
log("Hi, I'm" + name)
printMessage("hi,I'm " + name)
}
}
defined class MyUtil
defined trait Logger
defined class Person
scala> val p = new Person("sparks")
p: Person = Person@5bc44d78
// 既可以调用Logger中的方法也可以调用MyUtil中的方法
scala> p.sayHello
log: Hi, I'msparks
hi,I'm sparks

Scala入门系列(八):面向对象之trait的更多相关文章

  1. 微软云平台windows azure入门系列八课程

    微软云平台windows azure入门系列八课程: Windows Azure入门教学系列 (一): 创建第一个WebRole程序与部署 Windows Azure入门教学系列 (二): 创建第一个 ...

  2. C语言高速入门系列(八)

    C语言高速入门系列(八) C语言位运算与文件 本章引言: 在不知不觉中我们的C高速入门系列已经慢慢地接近尾声了,而在这一节中,我们会对 C语言中的位运算和文件进行解析,相信这两章对于一些人来说是陌生的 ...

  3. Scala入门系列(九):函数式编程

    引言 Scala是一门既面向对象,又面向过程的语言,Scala的函数式编程,就是Scala面向过程最好的佐证.也真是因此让Scala具备了Java所不具备的更强大的功能和特性. 而之所以Scala一直 ...

  4. Scala入门系列(六):面向对象之object

    object object相当于class的单个实例,类似于Java中的static,通常在里面放一些静态的field和method.   第一次调用object中的方法时,会执行object的con ...

  5. Scala入门系列(五):面向对象之类

    定义类 // 定义类,包含field以及method class HelloWorld { private var name = "Leo" def sayHello() { pr ...

  6. Scala入门系列(七):面向对象之继承

    extends 与Java一样,也是使用extends关键字,使用继承可以有效复用代码 class Person { private var name = "leo" def ge ...

  7. Scala入门系列(十):函数式编程之集合操作

    1. Scala的集合体系结构 Scala中的集合体系主要包括(结构跟Java相似): Iterable(所有集合trait的根trait) Seq(Range.ArrayBuffer.List等) ...

  8. Scala入门系列(一):基础语法

    Scala基础语法 Scala与JAVA的关系 Scala是基于Java虚拟机,也就是JVM的一门编程语言,所有Scala的代码都需要经过编译为字节码,然后交由Java虚拟机来运行. 所以Scala和 ...

  9. Scala入门系列(四):Map & Tuple

    Map 创建Map // 创建一个不可变的Map scala> val ages = Map("Leo" -> 30, "Sparks" -> ...

随机推荐

  1. Angular服务的5种创建方式

    config配置块 Angular应用的运行主要分为两部分:app.config()和app.run(),config是你设置任何的provider的阶段,从而使应用可以使用正确的服务,需要注意的是在 ...

  2. MySQL数据库储存bit类型的值报错

    当我们储存bit类型的值时,不能直接写入数字 上图中的画圈部分就是bit类型,若是直接填入"1"或"0"等等就会报错,如下: 这时候,我们要看bit(M)的M值 ...

  3. 使用angular4和asp.net core 2 web api做个练习项目(三)

    第一部分: http://www.cnblogs.com/cgzl/p/7755801.html 第二部分: http://www.cnblogs.com/cgzl/p/7763397.html 后台 ...

  4. C++ 中memset 勿要对类使用

    C++ 中memset 勿要对类使用 参考链接: http://www.cppblog.com/qinqing1984/archive/2009/08/07/92479.html 百度百科第一次这么给 ...

  5. 0_Simple__matrixMul + 0_Simple__matrixMul_nvrtc

    矩阵乘法,使用一维线程块和共享内存.并且在静态代码和运行时编译两种条件下使用. ▶ 源代码:静态使用 #include <stdio.h> #include <assert.h> ...

  6. web前端免费资源集

    web前端免费资源集 https://github.com/vhf/free-programming-books/blob/master/free-programming-books-zh.md

  7. web前端优化--DOM性能优化

    1.DOM访问与修改的优化: DOM访问是有代价的,修改DOM则会引起DOM的重绘与重排,而这两种操作会消耗性能. (1)缓存DOM:将频繁访问的对象或属性使用变量缓存起来,每次访问的时候,直接使用变 ...

  8. Django----中间件详解

    Django中间件 在http请求 到达视图函数之前   和视图函数return之后,django会根据自己的规则在合适的时机执行中间件中相应的方法. 中间件的执行流程 1.执行完所有的request ...

  9. ASP.Net Controls 用法大全

    The FindControl method of the System.Web.UI.Control class appears simple enough to use. In fact, the ...

  10. .Net主线程扑捉子线程中的异常

    首先看一段C#代码:运行后发现主线程通过try{}catch{}是不能扑捉子线程中的抛出来的异常. 代码 );        }        public void run()        {   ...