【Scala】Scala之Traits
一、前言
前面学习了Scala中包和导入的相关知识点,接着学习Traits(特质)
二、Traits
Scala的特质与Java的接口基本相同,当遇到可以使用Java接口的情形,就可以考虑使用特质,Scala的类可以使用extends和with关键字继承多个特质,如类或对象继承多个特质
class Woodpecker extends Bird with TreeScaling with Pecking
特质除了可以拥有Java中接口的抽象方法,同时还可以拥有已经实现的方法,可以将多余一个的特质混入类,并且特质可以控制哪些类可以混入该特质
2.1 将特质作为接口
1. 问题描述
像其他语言如Java创建接口一样,你想在Scala也创建类似东西
2. 解决方案
可以将特质类比为Java的接口,在特质中声明需要子类实现的方法
trait BaseSoundPlayer {
def play
def close
def pause
def stop
def resume
}
如果方法不带参数,则只需要写方法名即可,但若带参数,需要如下定义
trait Dog {
def speak(whatToSay: String)
def wagTail(enabled: Boolean)
}
当一个类继承特质时,需要使用extends和with关键字,当继承一个特质时,使用extends关键字
class Mp3SoundPlayer extends BaseSoundPlayer { ...}
继承多个特质时,使用extends和with关键字
class Foo extends BaseClass with Trait1 with Trait2 { ...}
除非实现特质的类是抽象的,否则其需要实现特质中所有方法
class Mp3SoundPlayer extends BaseSoundPlayer {
def play { // code here ... }
def close { // code here ... }
def pause { // code here ... }
def stop { // code here ... }
def resume { // code here ... }
}
如果没有实现所有抽象方法,则该类需要被申明为抽象的
abstract class SimpleSoundPlayer extends BaseSoundPlayer {
def play { ... }
def close { ... }
}
特质也可以集成其他特质
trait Mp3BaseSoundFilePlayer extends BaseSoundFilePlayer {
def getBasicPlayer: BasicPlayer
def getBasicController: BasicController
def setGain(volume: Double)
}
3. 讨论
特质可被作为Java的接口使用,并且在特质中申明需要被子类实现的方法,当继承特质时使用extends或者with关键字,当继承一个特质时,使用extends,若继承多个特质,则第一个使用extends,其他的使用with;若类继承类(抽象类)和特质,则抽象类使用extends,特质使用with,特质中可以有已经实现了的方法,如WaggingTail
abstract class Animal {
def speak
}
trait WaggingTail {
def startTail { println("tail started") }
def stopTail { println("tail stopped") }
}
trait FourLeggedAnimal {
def walk
def run
}
class Dog extends Animal with WaggingTail with FourLeggedAnimal {
// implementation code here ...
def speak { println("Dog says 'woof'") }
def walk { println("Dog is walking") }
def run { println("Dog is running") }
}
2.2 在特质中使用抽象和具体的字段
1. 问题描述
你想要在特质中定义抽象和具体的字段,以便继承特质的类可以使用字段
2. 解决方案
使用初始值赋值的字段是具体的,没有赋值的字段是抽象的
trait PizzaTrait {
var numToppings: Int // abstract
var size = 14 // concrete
val maxNumToppings = 10 // concrete
}
继承特质的类,需要初始化抽象字段,或者让该类为抽象类
class Pizza extends PizzaTrait {
var numToppings = 0 // 'override' not needed
size = 16 // 'var' and 'override' not needed
}
3. 讨论
特质中的字段可以被声明为val或var,你不需要使用override字段来覆盖var字段,但是需要使用override来覆盖val字段
trait PizzaTrait {
val maxNumToppings: Int
}
class Pizza extends PizzaTrait {
override val maxNumToppings = 10 // 'override' is required
}
2.3 像抽象类一样使用特质
1. 问题描述
你想要像Java中的抽象类一样使用特质
2. 解决方案
在特质中定义方法,继承特质的类中,可以覆盖该方法,或者是直接使用
trait Pet {
def speak { println("Yo") } // concrete implementation
def comeToMaster // abstract method
}
class Dog extends Pet {
// don't need to implement 'speak' if you don't need to
def comeToMaster { ("I'm coming!") }
}
class Cat extends Pet {
// override the speak method
override def speak { ("meow") }
def comeToMaster { ("That's not gonna happen.") }
}
如果一个类没有实现特质的抽象方法,那么它也需要被声明为抽象的
abstract class FlyingPet extends Pet {
def fly { ("I'm flying!") }
}
3. 讨论
一个类仅仅只能继承一个抽象类,但是可以继承多个特质,使用特质更为灵活
2.4 把特质作为简单的混合物
1. 问题描述
你想要将多个特质混合进一个类中
2. 解决方案
为实现简单的混合,在特质中定义方法,然后使用extends和with继承特质,如定义Tail特质
trait Tail {
def wagTail { println("tail is wagging") }
def stopTail { println("tail is stopped") }
}
可以使用该特质和抽象的Pet类来创建Dog类
abstract class Pet (var name: String) {
def speak // abstract
def ownerIsHome { println("excited") }
def jumpForJoy { println("jumping for joy") }
}
class Dog (name: String) extends Pet (name) with Tail {
def speak { println("woof") }
override def ownerIsHome {
wagTail
speak
}
}
Dog类通知拥有特质Tail和抽象类Pet的行为
2.5 通过继承控制哪个类可以使用特质
1. 问题描述
你想限制的特性,只能将其添加至一个父类或者另一个特质的类
2. 解决方案
使用下面语法声明名为TraitName的特质,而TraitName只能被混入继承了SuperThing的类,SuperThing可以是一个特质、抽象类、类。
trait [TraitName] extends [SuperThing]
例如,Starship和StarfleetWarpCore都继承了StarfleetComponent,所以StarfleetWarpCore特质可以被混入Starship中
class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class Starship extends StarfleetComponent with StarfleetWarpCore
然而,Warbird不能继承StarfleetWarpCore特质,因为其不继承StarfleetComponent类
class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponent
class RomulanStuff // won't compile
class Warbird extends RomulanStuff with StarfleetWarpCore
3. 讨论
一个特质继承一个类不是一种普遍情况,但是当其发生时,需要保证拥有相同的父类
2.6 标记特质以使得其仅仅能被某种类型子类使用
1. 问题描述
你想要标记您的特性,因此只能用于扩展给定基类型的类型
2. 解决方案
保证MyTrait的特质仅仅只能被混入BaseType的子类,可以使用this: BaseType =>声明开始特质
trait MyTrait {
this: BaseType =>
例如,为了是StarfleetWarpCore特质只能用于Starship,可以标记StarfleetWarpCore特质如下
trait StarfleetWarpCore {
this: Starship =>
// more code here ...
}
给定上面的定义,下面代码将会运行良好
class Starship
class Enterprise extends Starship with StarfleetWarpCore
而如下代码则编译错误
class RomulanShip
// this won't compile
class Warbird extends RomulanShip with StarfleetWarpCore
3. 讨论
任何混入特质的具体类型需要保证其能够转化为特质的自我类型,特质也可要求继承其的子类必须继承多种类型
trait WarpCore {
this: Starship with WarpCoreEjector with FireExtinguisher =>
}
如下代码中的Enterprise将会通过编译,因为其签名满足特质的定义
class Starship
trait WarpCoreEjector
trait FireExtinguisher
// this works
class Enterprise extends Starship
with WarpCore
with WarpCoreEjector
with FireExtinguisher
若Enterprise不继承特质,则无法满足签名,会报错
2.7 保证特质只能被混入含有某特定方法的类
1. 问题描述
你想要将特质混入包含了某种方法签名的类
2. 解决方案
WarpCore特质要求其所混入的类必须包含ejectWrapCore方法
trait WarpCore {
this: { def ejectWarpCore(password: String): Boolean } =>
}
Enterprise类满足要求,编译成功
class Starship {
// code here ...
}
class Enterprise extends Starship with WarpCore {
def ejectWarpCore(password: String): Boolean = {
if (password == "password") {
println("ejecting core")
true
} else {
false
}
}
}
特质可以要求混入的类包含多个方法
trait WarpCore {
this: {
def ejectWarpCore(password: String): Boolean
def startWarpCore: Unit
} =>
}
class Starship
class Enterprise extends Starship with WarpCore {
def ejectWarpCore(password: String): Boolean = {
if (password == "password") { println("core ejected"); true } else false
}
def startWarpCore { println("core started") }
}
3. 讨论
该方法也被称为结构类型,因为你规定了某些类必须具有的结构
2.8 将特质添加至对象实例
1. 问题描述
当对象实例创建时,你想要混入特质
2. 解决方案
可以使用如下方法
class DavidBanner
trait Angry {
println("You won't like me ...")
}
object Test extends App {
val hulk = new DavidBanner with Angry
}
当运行代码时,会出现You won't like me ...结果,因为Angry特质会被实例化
3. 讨论
混入debugging和logging可能是更为常用的用法
trait Debugger {
def log(message: String) {
// do something with message
}
}
// no debugger
val child = new Child
// debugger added as the object is created
val problemChild = new ProblemChild with Debugger
2.9 像特质一样继承Java接口
1. 问题描述
你想要在Scala应用中实现Java接口
2. 解决方案
可以使用extends和with关键字继承接口,如同继承特质一样,给定如下Java代码
// java
public interface Animal {
public void speak();
} public interface Wagging {
public void wag();
} public interface Running {
public void run();
}
你可以以Scala方式创建Dog类
// scala
class Dog extends Animal with Wagging with Running {
def speak { println("Woof") }
def wag { println("Tail is wagging!") }
def run { println("I'm running!") }
}
区别在于Java接口不能实现方法,所以当继承接口时,要么实现所有方法,要么声明为抽象的
三、总结
本篇博文讲解了Scala中的特质相关点,其可以类比于Java的接口,但是比接口更为灵活,如可添加字段和已经实现方法(在Java 8后的接口也可以添加已实现的方法),谢谢各位园友的观看~
【Scala】Scala之Traits的更多相关文章
- [Scala] Scala基础知识
Object An object is a type of class that can have no more than one instance, known in object-oriente ...
- [scala] scala 集合(⑧)
1.List 基础操作 2.Set 基础操作 3. TreeSet 排序Set 4. 拉链操作 5. 流 import scala.collection.immutable.TreeSet impor ...
- [scala] scala 函数 (⑦)
1.scala 函数定义 2.scala 高阶函数 3.匿名函数 4.柯里化 import scala.math._ /** * @author xwolf * @date 2017-04-24 9: ...
- [Scala]Scala学习笔记七 正则表达式
1. Regex对象 我们可以使用scala.util.matching.Regex类使用正则表达式.要构造一个Regex对象,使用String类的r方法即可: val numPattern = &q ...
- [Scala]Scala学习笔记六 文件
1. 读取行 读取文件,可以使用scala.io.Source对象的fromFile方法.如果读取所有行可以使用getLines方法: val source = Source.fromFile(&qu ...
- [Scala]Scala学习笔记五 Object
1. 单例对象 Scala没有静态方法或静态字段,可以使用object来达到这个目的,对象定义了某个类的单个实例: object Account{ private var lastNumber = 0 ...
- [Scala]Scala学习笔记四 类
1. 简单类与无参方法 class Person { var age = 0 // 必须初始化字段 def getAge() = age // 方法默认为公有的 } 备注 在Scala中,类并不声明为 ...
- Scala:scala的一些简单操作命令
Scala是一门多范式的编程语言,一种类似java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. 不太久之前编程语言还可以毫无疑意地归类成“命令式”或者“函数式”或 ...
- [Scala]Scala学习笔记三 Map与Tuple
1. 构造映射 可以使用如下命令构造一个映射: scala> val scores = Map("Alice" -> 90, "Kim" -> ...
- [Scala]Scala学习笔记二 数组
1. 定长数组 如果你需要一个长度不变的数组,可以使用Scala中的Array. val nums = new Array[Int](10) // 10个整数的数组 所有元素初始化为0 val str ...
随机推荐
- nodeJS中读写文件方法的区别
导言:nodejs中所有与文件相关的操作都在fs模块中,而读写操作又是我们会经常用到的操作,nodejs的fs模块针对读操作为我们提供了readFile,read, createReadStream三 ...
- c风格字符串,字符串字面值,c++字符串
C风格字符串:本质上就是以空字符null为结束符的数组 可以简单的理解为:有'\0'的是c风格字符串,无'\0'的是普通字符数组 字符串字面值:是一串常量字符,字符串字面值常量用双引号括起来的零个或多 ...
- 读书笔记 effective c++ Item 29 为异常安全的代码而努力
异常安全在某种意义上来说就像怀孕...但是稍微想一想.在没有求婚之前我们不能真正的讨论生殖问题. 假设我们有一个表示GUI菜单的类,这个GUI菜单有背景图片.这个类将被使用在多线程环境中,所以需要mu ...
- 我在ubuntu桌面系统下进行WEB开发常用的软件
公司电脑本来是win系统的,不知道怎么突然中毒了,由于比较讨厌杀毒软件,所以之前都被我卸载掉了,所以我干脆重装了一个ubuntu系统 1.IDE vscode 各种插件,自带Git,markdo ...
- JAVA-FTP批量大文件传输
FTP的具体使用 FTP是一种网络协议,用于进行不同服务器主机之间的文件传输,或者简单地说两台不同IP的机器之间的文件传输.在java中我们什么时候需要用到FTP文件传输呢?比如两台服务器的 ...
- Ionic2 开发笔记(1)ionic2 +angular2搭建
1.去node.js官网下载对应版本https://nodejs.org/en/(不会请看OnsenUi搭载,里面有详细过程) 2.然后通过npm下载Ionic和cordova $ npm insta ...
- SQL server 数据库——表连接(多表横向连接,纵向连接)
表连接 1.select * from student,score --笛卡尔积 2.两个表的连接: 法1:select student.sno, sname, degree from student ...
- 1084: [SCOI2005]最大子矩阵
1084: [SCOI2005]最大子矩阵 Time Limit: 10 Sec Memory Limit: 162 MBSubmit: 1325 Solved: 670[Submit][Stat ...
- TypeScript设计模式之中介者、观察者
看看用TypeScript怎样实现常见的设计模式,顺便复习一下. 学模式最重要的不是记UML,而是知道什么模式可以解决什么样的问题,在做项目时碰到问题可以想到用哪个模式可以解决,UML忘了可以查,思想 ...
- MySQL查询语句的45道练习
一.设有一数据库,包括四个表:学生表(Student).课程表(Course).成绩表(Score)以及教师信息表(Teacher).四个表的结构分别如表1-1的表(一)~表(四) ...