一、为什么没有多重继承

c++允许多重继承

Java不允许多重继承,类只能继承一个超类,可以实现任意数量的接口。

如何继承这两个抽象基类?

Scala提供“特质”而非接口;特质可以同时抽象方法和具体方法。类可以实现多个特质。

二、当做接口使用的特质

  trait Logger{
def log(msg: String)
} class ConsoleLogger extends Logger{ //使用 extends 不能使用 implements def log(msg: String): Unit ={ // 不需要使用override
println(msg)
}
}

三、带有具体实现的特质

  trait Logger{
def log(msg: String){println(msg)}
} class SavingAccount extends Logger{
def display(msg: String): Unit ={
log(msg)
}
} val v = new SavingAccount()
v.display("Hello")

注:特质发生变化,所有混入特质的类都需要重新编译。

四、带有特质的对象

  trait Logged{
def log(msg: String){} //特质,方法是个空实现
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
} class SavingAccount extends Logged{ //类继承特质,方法的实现结果为空
def display(msg:String){log(msg)
}
} val s = new SavingAccount() //构造对象,结果为空
s.display("Hello") val s1 = new SavingAccount() with ConsoleLogger //在构造对象的时候,加入特质,结果为 Hello
s1.display("Hello")

五、叠加在一起的特质

trait Logged{
def log(msg: String){}
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
} trait TimestampLogger extends Logged{
override def log(msg: String){
super.log(new java.util.Date() + " " + msg)
}
} trait ShortLogger extends Logged{
val maxLength = 15
override def log(msg: String){
super.log(
if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
)
}
} class SavingAccount extends Logged {
def display(msg: String) {
log(msg)
}
} val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger //ShortLogger的log方法先执行,然后是 TimestampLogger
s.display("Hello") val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger //TimeStampLogger的方法先执行,然后是 ShortLogger
s1.display("Hello")

结果:

Wed Aug 31 23:27:53 CST 2016 Hello
Wed Aug 31 2...

六、在特质中重写抽象方法

    trait Logged{
def log(msg: String)
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
}
trait TimestampLogger extends Logged{
abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
super.log(new java.util.Date() + " " + msg)
}
} class SavingAccount extends ConsoleLogger {
def display(msg: String) {
log(msg)
}
} val s = new SavingAccount with TimestampLogger
s.display("Hello")

结果:

Wed Aug 31 23:51:40 CST 2016 Hello

七、当做富接口使用的特质

    trait Logged{                           // 特质将抽象方法,和具体方法结合在一起
def log(msg: String)
def info(msg: String){log(msg)}
def warn(msg: String){log(msg)}
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
}
trait TimestampLogger extends Logged{
abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
super.log(new java.util.Date() + " " + msg)
}
} class SavingAccount extends ConsoleLogger {
def display(msg: String) {
info(msg)
}
} val s = new SavingAccount with TimestampLogger
s.display("Hello")

结果:

Thu Sep 01 09:38:54 CST 2016 Hello

八、特质中的具体字段

在子类中添加特质,特质中的字段就相当于子类中的字段

九、特质中的抽象字段

特质中的抽象字段,在子类的具体实现中必须要被重写

    trait Logged{                           // 特质将抽象方法,和具体方法结合在一起
val maxLength: Int // 抽象字段
def log(msg: String)
def info(msg: String){log(msg)}
def warn(msg: String){log(msg)}
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
}
trait TimestampLogger extends Logged{
abstract override def log(msg: String){ //继承抽象方法,需要使用abstract关键字,对抽象方法进行重写
super.log(new java.util.Date() + " " + msg)
}
} class SavingAccount(val maxLength: Int) extends ConsoleLogger { //抽象字段当做参数传递
//val maxLength = 20 //抽象字段在类中被重写
def display(msg: String) {
info(msg)
info(maxLength.toString)
}
} val s = new SavingAccount(20) with TimestampLogger //传递参数
s.display("Hello")

结果:

Thu Sep 01 10:05:55 CST 2016 Hello
Thu Sep 01 10:05:55 CST 2016 20

十、特质构造顺序

1、首先调用超类的构造器

2、然后调用特质构造器,特质构造器在超类构造器之后,类构造器之前

3、特质由左到右被构造

4、在每个特质当中,父特质先被构造

5、如果多个特质公用一个父特质,而那个父特质已经被构造过了,则不会再被构造

6、所有特质构造完毕,子类被构造

例子:

class SavingAccount extends Account with FileLogger with ShortLogger

构造器执行顺序:

1、Account(超类)

2、Logger(第一个特质的父特质)

3、FileLogger(第一个特质)

4、ShortLogger(第二个特质)

5、SavingAccount(类)

特质方法Super被解析的顺序 从右向左

ShortLogger(super)->FileLogger(super)->Logger

  trait Logged{
def log(msg: String){}
} trait ConsoleLogger extends Logged{
override def log(msg: String) {println(msg)}
} trait TimestampLogger extends Logged{
override def log(msg: String){
println("This is TimestampLogger")
super.log(new java.util.Date() + " " + msg)
}
} trait ShortLogger extends Logged{
val maxLength = 15
override def log(msg: String){
println("This is ShortLogger")
super.log(
if(msg.length <= maxLength) msg else msg.substring(0, maxLength-3) + "..."
)
}
} class SavingAccount extends Logged {
def display(msg: String) {
log(msg)
}
} val s = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger
s.display("Hello") val s1 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger
s1.display("Hello")

结果:

This is ShortLogger
This is TimestampLogger
Thu Sep 01 13:55:33 CST 2016 Hello
This is TimestampLogger
This is ShortLogger
Thu Sep 01 1...

十一、初始化特质中的字段

特质中不能使用构造参数

要想初始化特质中的字段,可使用如下方式:

1、提前定义,在特质的构造函数之前定义

  trait Logger{
def log(msg: String){}
} trait FileLogger extends Logger{
val filename: String
val out = new PrintWriter(filename)
override def log(msg: String) {
out.println(msg)
out.flush()
}
} class SavingAccount extends Logger{
def display(msg: String): Unit ={
log(msg)
}
}//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行 c.display("hello11")

2、使用lazy值

  trait Logger{
def log(msg: String){}
} trait FileLogger extends Logger{
val filename: String
lazy val out = new PrintWriter(filename) //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
override def log(msg: String) {
out.println(msg)
out.flush()
}
} class SavingAccount extends Logger{
def display(msg: String): Unit ={
log(msg)
}
}//val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
//val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
val c = new SavingAccount with FileLogger { val filename="mylog.txt" } c.display("hello world")

懒值在每次使用前都会检查是否已经初始化,用起来并不是那么高效

3、使用类主构造器传参

  trait Logger{
def log(msg: String){}
} trait FileLogger extends Logger{
val filename: String
//lazy val out = new PrintWriter(filename) //使用懒值 out 在使用时,才会初始化,那时filename值已经被正确设置了
val out = new PrintWriter(filename)
override def log(msg: String) {
out.println(msg)
out.flush()
}
} // class SavingAccount extends Logger{
// def display(msg: String): Unit ={
// log(msg)
// }
// } class SavingAccount(val filename: String) extends Logger{
def display(msg: String): Unit ={
log(msg)
}
} //val c = new SavingAccount with FileLogger{ val filename="mylog.txt" } //构造器运行在 FileLogger构造器之后,所以不会运行成功
//val c = new {val filename = "mylog.txt"} with SavingAccount with FileLogger //这里使用了提前定义,可以正确执行
//val c = new SavingAccount with FileLogger { val filename="mylog.txt" }
val c = new SavingAccount("mylog.txt") with FileLogger c.display("hello world 111")

十二、扩展类的特质

特质可以扩展另一个特质,特质也可以扩展类

类扩展了特质,特质扩展了类,特质的超类成为我们类的超类;

如果类扩展了另一个类,只要那个类是特质超类的子类就可以。

如果扩展多个类,且是不相干的,类不能有多个超类

  trait Logged{
def log(msg: String){}
} trait LoggedException extends Exception with Logged{ //特质扩展超类
def log() {log(getMessage())}
} class UnhappyException extends LoggedException{ //类扩展特质,特质的超类Exception 也成为了类UnHappyException的超类
override def getMessage() = "aaa"
} class UnhappyException extends IOException with LoggedException //可以扩展 IOException是 Exception的子类 class UnhappyException extends JFrame with LoggedException // JFrame 和 Exception 没继承关系 不能扩展

十三、自身类型

this : Exception =>

    trait Logged{
def log(msg: String){}
} trait LoggedException extends Logged{ //特质扩展超类
this: Exception => //自身类型 自身类型为Exception,它只能被混入Exception的子类
def log() {log(getMessage())}
} val f = new JFrame with LoggedException //错误 JFrame不是 Exception的子类型,而Exception是LoggedException的自身类型

结构类型:

  trait Logged{
def log(msg: String){}
} trait LoggedException extends Logged{ //特质扩展超类
this: {def getMessage():String} => //自身类型(结构类型) 这个特质可以被混入任何拥有getMessage方法的类
def log() {log(getMessage())}
}

十四、背后发生了什么

参考《快学Scala》

Scala--特质的更多相关文章

  1. scala特质

    package com.ming.test /** * scala 特质,类似与java接口,但是比java接口强大,可以有实现方法,定义字段之类的 */ /** * 定义一个日志的特质 */ tra ...

  2. Scala 特质全面解析

    要点如下: Scala中类只能继承一个超类, 可以扩展任意数量的特质 特质可以要求实现它们的类具备特定的字段, 方法和超类 与Java接口不同, Scala特质可以提供方法和字段的实现 当将多个特质叠 ...

  3. scala akka 修炼之路5(scala特质应用场景分析)

    scala中特质定义:包括一些字段,行为(方法/函数/动作)和一些未实现的功能接口的集合,能够方便的实现扩展或混入到已有类或抽象类中. scala中特质(trait)是一个非常实用的特性,在程序设计中 ...

  4. 8.scala:特质

    版权申明:转载请注明出处.文章来源:http://bigdataer.net/?p=317 总体来说,scala中的特质类似于Java中的接口,但是有别于接口的是特质中既可以有实现方法也可以有抽象方法 ...

  5. scala 特质的应用

    一.为类提供可以堆叠的改变 package com.jason.qianfeng trait Loggertest { def logger(msg: String) } trait ConsoleL ...

  6. scala学习笔记——特质

    一个类扩展自一个或多个特质,以便使用这些特质提供的服务.特质可能会要求使用它的类支持某个特定的特性.不过和java不同,Scala特质可以给出这些特性的缺省实现. 特质的特性: 类可以实现任意数量的特 ...

  7. 快学Scala之特质

    一个Scala类可以继承多个特质(trait), 特质可能会要求使用它们的类支持某个特定特性, 与Java接口不同, Scala特质可以给出这些特质的缺省实现. 要点如下: Scala中类只能继承一个 ...

  8. Scala入门2(特质与叠加在一起的特质)

    一.介绍 参考http://luchunli.blog.51cto.com/2368057/1705025 我们知道,如果几个类有某些共通的方法或者字段,那么从它们多重继承时,就会出现麻烦.所以Jav ...

  9. 快学Scala习题解答—第十章 特质

    10 特质 10.1 java.awt.Rectangle类有两个非常实用的方法translate和grow,但可惜的是像java.awt.geom.Ellipse2D这种类没有. 在Scala中,你 ...

  10. Scala学习十——特质

    一.本章要点 类可以实现任意数量的特质 特质可以要求实现它们的类具备特定的字段,方法或超类 和Java接口不同,Scala特质可以提供方法和字段实现 当你将多个特质叠加在一起时,顺序很重要——其方法先 ...

随机推荐

  1. CSS3实现的几个小loading效果

    昨晚上闲的没事突然想做几个小loading效果,下面是昨晚上做的几个小案例,分享给大家 1.水波loading:这个loading是我觉得非常简单,但是看上去的效果却非常不错的一个小loading 这 ...

  2. 【读书笔记】iOS-网络-HTTP-请求内容

    一,GET方法. 从服务器获取一段内容,用HTTP术语来说就是实体.GET请求通常不包含请求体,不过也是可以包含的.有些网络缓存设施只会缓存GET响应.GET请求通常不会导致服务器端的数据变化. 二, ...

  3. Sqoop安装与应用过程

    1.  参考说明 参考文档: http://sqoop.apache.org/ http://sqoop.apache.org/docs/1.99.7/admin/Installation.html ...

  4. Monkey Android app稳定性测试工具之Monkey使用教程

    Monkey Android app稳定性测试工具之Monkey使用教程 by:授客 QQ:1033553122 由于篇幅问题,仅提供百度网盘下载链接: Android app稳定性测试工具之Monk ...

  5. WOSA/XFS PTR Form解析库—xfsptrdata.h

    #ifndef _XFSPTRDATA_H_#define _XFSPTRDATA_H_ #include <XFSPTR.H>#include <Windows.h>#inc ...

  6. ethernaut 以太坊靶场学习 (1-12)

    前言 这个靶场搜集了许多不同的 solidity 开发的问题,通过这个可以入门 区块链安全 Fallback 给出了源码 pragma solidity ^0.4.18; import 'zeppel ...

  7. javascript的异步编程解决方案收集

    缘起 没理解js异步的同学看下面的例子: for (var i = 0; i < 5; i++) { //模拟一个异步操作 setTimeout(() => { console.log(i ...

  8. Android--PullToRefreshListView的onRefreshComplete()不起作用的问题

    今天用到了网上开源的下拉刷新组件PullToRefreshListView的第三方下拉刷新的ListView 我们发现 有时候我们当使用它的onRefreshComplete()方法是,我们下拉出来的 ...

  9. 抽象数据类型(ADT)

    概念 抽象数据类型(ADT),脱离具体实现定义数据结构,它的定义着重于做什么,而忽略怎么做 举例 列表.栈.队列 列表 列表,也叫线性表 抽象定义:数据项线性排列,可以插入某一项,删除某一项,读取某一 ...

  10. LeetCode题解之Longest Continuous Increasing Subsequence

    1.题目描述 2.问题分析 从每一个num[i]往前扫描即可. 3.代码 int findLengthOfLCIS(vector<int>& nums) { ){ return n ...