一、前言

  前面学习了Scala中包和导入的相关知识点,接着学习Traits(特质)

二、Traits

  Scala的特质与Java的接口基本相同,当遇到可以使用Java接口的情形,就可以考虑使用特质,Scala的类可以使用extends和with关键字继承多个特质,如类或对象继承多个特质  

  1. class Woodpecker extends Bird with TreeScaling with Pecking

  特质除了可以拥有Java中接口的抽象方法,同时还可以拥有已经实现的方法,可以将多余一个的特质混入类,并且特质可以控制哪些类可以混入该特质

  2.1 将特质作为接口

  1. 问题描述

  像其他语言如Java创建接口一样,你想在Scala也创建类似东西

  2. 解决方案

  可以将特质类比为Java的接口,在特质中声明需要子类实现的方法 

  1. trait BaseSoundPlayer {
  2. def play
  3. def close
  4. def pause
  5. def stop
  6. def resume
  7. }

  如果方法不带参数,则只需要写方法名即可,但若带参数,需要如下定义  

  1. trait Dog {
  2. def speak(whatToSay: String)
  3. def wagTail(enabled: Boolean)
  4. }

  当一个类继承特质时,需要使用extends和with关键字,当继承一个特质时,使用extends关键字  

  1. class Mp3SoundPlayer extends BaseSoundPlayer { ...}

  继承多个特质时,使用extends和with关键字

  1. class Foo extends BaseClass with Trait1 with Trait2 { ...}

  除非实现特质的类是抽象的,否则其需要实现特质中所有方法 

  1. class Mp3SoundPlayer extends BaseSoundPlayer {
  2. def play { // code here ... }
  3. def close { // code here ... }
  4. def pause { // code here ... }
  5. def stop { // code here ... }
  6. def resume { // code here ... }
  7. }

  如果没有实现所有抽象方法,则该类需要被申明为抽象的

  1. abstract class SimpleSoundPlayer extends BaseSoundPlayer {
  2. def play { ... }
  3. def close { ... }
  4. }

  特质也可以集成其他特质  

  1. trait Mp3BaseSoundFilePlayer extends BaseSoundFilePlayer {
  2. def getBasicPlayer: BasicPlayer
  3. def getBasicController: BasicController
  4. def setGain(volume: Double)
  5. }

  3. 讨论

  特质可被作为Java的接口使用,并且在特质中申明需要被子类实现的方法,当继承特质时使用extends或者with关键字,当继承一个特质时,使用extends,若继承多个特质,则第一个使用extends,其他的使用with;若类继承类(抽象类)和特质,则抽象类使用extends,特质使用with,特质中可以有已经实现了的方法,如WaggingTail 

  1. abstract class Animal {
  2. def speak
  3. }
  4.  
  5. trait WaggingTail {
  6. def startTail { println("tail started") }
  7. def stopTail { println("tail stopped") }
  8. }
  9.  
  10. trait FourLeggedAnimal {
  11. def walk
  12. def run
  13. }
  14.  
  15. class Dog extends Animal with WaggingTail with FourLeggedAnimal {
  16. // implementation code here ...
  17. def speak { println("Dog says 'woof'") }
  18. def walk { println("Dog is walking") }
  19. def run { println("Dog is running") }
  20. }

  2.2 在特质中使用抽象和具体的字段

  1. 问题描述

  你想要在特质中定义抽象和具体的字段,以便继承特质的类可以使用字段

  2. 解决方案

  使用初始值赋值的字段是具体的,没有赋值的字段是抽象的

  1. trait PizzaTrait {
  2. var numToppings: Int // abstract
  3. var size = 14 // concrete
  4. val maxNumToppings = 10 // concrete
  5. }

  继承特质的类,需要初始化抽象字段,或者让该类为抽象类 

  1. class Pizza extends PizzaTrait {
  2. var numToppings = 0 // 'override' not needed
  3. size = 16 // 'var' and 'override' not needed
  4. }

  3. 讨论

  特质中的字段可以被声明为val或var,你不需要使用override字段来覆盖var字段,但是需要使用override来覆盖val字段 

  1. trait PizzaTrait {
  2. val maxNumToppings: Int
  3. }
  4.  
  5. class Pizza extends PizzaTrait {
  6. override val maxNumToppings = 10 // 'override' is required
  7. }

  2.3 像抽象类一样使用特质

  1. 问题描述

  你想要像Java中的抽象类一样使用特质

  2. 解决方案

  在特质中定义方法,继承特质的类中,可以覆盖该方法,或者是直接使用

  1. trait Pet {
  2. def speak { println("Yo") } // concrete implementation
  3. def comeToMaster // abstract method
  4. }
  5.  
  6. class Dog extends Pet {
  7. // don't need to implement 'speak' if you don't need to
  8. def comeToMaster { ("I'm coming!") }
  9. }
  10.  
  11. class Cat extends Pet {
  12. // override the speak method
  13. override def speak { ("meow") }
  14. def comeToMaster { ("That's not gonna happen.") }
  15. }

  如果一个类没有实现特质的抽象方法,那么它也需要被声明为抽象的

  1. abstract class FlyingPet extends Pet {
  2. def fly { ("I'm flying!") }
  3. }

  3. 讨论

  一个类仅仅只能继承一个抽象类,但是可以继承多个特质,使用特质更为灵活

  2.4 把特质作为简单的混合物

  1. 问题描述

  你想要将多个特质混合进一个类中

  2. 解决方案

  为实现简单的混合,在特质中定义方法,然后使用extends和with继承特质,如定义Tail特质 

  1. trait Tail {
  2. def wagTail { println("tail is wagging") }
  3. def stopTail { println("tail is stopped") }
  4. }

  可以使用该特质和抽象的Pet类来创建Dog类  

  1. abstract class Pet (var name: String) {
  2. def speak // abstract
  3. def ownerIsHome { println("excited") }
  4. def jumpForJoy { println("jumping for joy") }
  5. }
  6.  
  7. class Dog (name: String) extends Pet (name) with Tail {
  8. def speak { println("woof") }
  9. override def ownerIsHome {
  10. wagTail
  11. speak
  12. }
  13. }

  Dog类通知拥有特质Tail和抽象类Pet的行为

  2.5 通过继承控制哪个类可以使用特质

  1. 问题描述

    你想限制的特性,只能将其添加至一个父类或者另一个特质的类

  2. 解决方案

  使用下面语法声明名为TraitName的特质,而TraitName只能被混入继承了SuperThing的类,SuperThing可以是一个特质、抽象类、类。  

  1. trait [TraitName] extends [SuperThing]

  例如,Starship和StarfleetWarpCore都继承了StarfleetComponent,所以StarfleetWarpCore特质可以被混入Starship中  

  1. class StarfleetComponent
  2. trait StarfleetWarpCore extends StarfleetComponent
  3. class Starship extends StarfleetComponent with StarfleetWarpCore

  然而,Warbird不能继承StarfleetWarpCore特质,因为其不继承StarfleetComponent类 

  1. class StarfleetComponent
  2. trait StarfleetWarpCore extends StarfleetComponent
  3. class RomulanStuff
  4.  
  5. // won't compile
  6. class Warbird extends RomulanStuff with StarfleetWarpCore

  3. 讨论

  一个特质继承一个类不是一种普遍情况,但是当其发生时,需要保证拥有相同的父类

  2.6 标记特质以使得其仅仅能被某种类型子类使用

  1. 问题描述

   你想要标记您的特性,因此只能用于扩展给定基类型的类型

  2. 解决方案

  保证MyTrait的特质仅仅只能被混入BaseType的子类,可以使用this: BaseType =>声明开始特质 

  1. trait MyTrait {
  2. this: BaseType =>

  例如,为了是StarfleetWarpCore特质只能用于Starship,可以标记StarfleetWarpCore特质如下

  1. trait StarfleetWarpCore {
  2. this: Starship =>
  3. // more code here ...
  4. }

  给定上面的定义,下面代码将会运行良好  

  1. class Starship
  2. class Enterprise extends Starship with StarfleetWarpCore

  而如下代码则编译错误  

  1. class RomulanShip
  2. // this won't compile
  3. class Warbird extends RomulanShip with StarfleetWarpCore

  3. 讨论

  任何混入特质的具体类型需要保证其能够转化为特质的自我类型,特质也可要求继承其的子类必须继承多种类型

  1. trait WarpCore {
  2. this: Starship with WarpCoreEjector with FireExtinguisher =>
  3. }

  如下代码中的Enterprise将会通过编译,因为其签名满足特质的定义  

  1. class Starship
  2. trait WarpCoreEjector
  3. trait FireExtinguisher
  4. // this works
  5. class Enterprise extends Starship
  6. with WarpCore
  7. with WarpCoreEjector
  8. with FireExtinguisher

  若Enterprise不继承特质,则无法满足签名,会报错

  2.7 保证特质只能被混入含有某特定方法的类

  1. 问题描述

  你想要将特质混入包含了某种方法签名的类

  2. 解决方案

  WarpCore特质要求其所混入的类必须包含ejectWrapCore方法 

  1. trait WarpCore {
  2. this: { def ejectWarpCore(password: String): Boolean } =>
  3. }

  Enterprise类满足要求,编译成功

  1. class Starship {
  2. // code here ...
  3. }
  4.  
  5. class Enterprise extends Starship with WarpCore {
  6. def ejectWarpCore(password: String): Boolean = {
  7. if (password == "password") {
  8. println("ejecting core")
  9. true
  10. } else {
  11. false
  12. }
  13. }
  14. }

  特质可以要求混入的类包含多个方法 

  1. trait WarpCore {
  2. this: {
  3. def ejectWarpCore(password: String): Boolean
  4. def startWarpCore: Unit
  5. } =>
  6. }
  7.  
  8. class Starship
  9.  
  10. class Enterprise extends Starship with WarpCore {
  11. def ejectWarpCore(password: String): Boolean = {
  12. if (password == "password") { println("core ejected"); true } else false
  13. }
  14.  
  15. def startWarpCore { println("core started") }
  16. }

  3. 讨论

  该方法也被称为结构类型,因为你规定了某些类必须具有的结构

  2.8 将特质添加至对象实例

  1. 问题描述

  当对象实例创建时,你想要混入特质

  2. 解决方案

  可以使用如下方法  

  1. class DavidBanner
  2.  
  3. trait Angry {
  4. println("You won't like me ...")
  5. }
  6.  
  7. object Test extends App {
  8. val hulk = new DavidBanner with Angry
  9. }

  当运行代码时,会出现You won't like me ...结果,因为Angry特质会被实例化

  3. 讨论

  混入debugging和logging可能是更为常用的用法 

  1. trait Debugger {
  2. def log(message: String) {
  3. // do something with message
  4. }
  5. }
  6.  
  7. // no debugger
  8. val child = new Child
  9.  
  10. // debugger added as the object is created
  11. val problemChild = new ProblemChild with Debugger

  2.9 像特质一样继承Java接口

  1. 问题描述

  你想要在Scala应用中实现Java接口

  2. 解决方案

  可以使用extends和with关键字继承接口,如同继承特质一样,给定如下Java代码

  1. // java
  2. public interface Animal {
  3. public void speak();
  4. }
  5.  
  6. public interface Wagging {
  7. public void wag();
  8. }
  9.  
  10. public interface Running {
  11. public void run();
  12. }

  你可以以Scala方式创建Dog类

  1. // scala
  2. class Dog extends Animal with Wagging with Running {
  3. def speak { println("Woof") }
  4. def wag { println("Tail is wagging!") }
  5. def run { println("I'm running!") }
  6. }

  区别在于Java接口不能实现方法,所以当继承接口时,要么实现所有方法,要么声明为抽象的

三、总结

  本篇博文讲解了Scala中的特质相关点,其可以类比于Java的接口,但是比接口更为灵活,如可添加字段和已经实现方法(在Java 8后的接口也可以添加已实现的方法),谢谢各位园友的观看~ 

【Scala】Scala之Traits的更多相关文章

  1. [Scala] Scala基础知识

    Object An object is a type of class that can have no more than one instance, known in object-oriente ...

  2. [scala] scala 集合(⑧)

    1.List 基础操作 2.Set 基础操作 3. TreeSet 排序Set 4. 拉链操作 5. 流 import scala.collection.immutable.TreeSet impor ...

  3. [scala] scala 函数 (⑦)

    1.scala 函数定义 2.scala 高阶函数 3.匿名函数 4.柯里化 import scala.math._ /** * @author xwolf * @date 2017-04-24 9: ...

  4. [Scala]Scala学习笔记七 正则表达式

    1. Regex对象 我们可以使用scala.util.matching.Regex类使用正则表达式.要构造一个Regex对象,使用String类的r方法即可: val numPattern = &q ...

  5. [Scala]Scala学习笔记六 文件

    1. 读取行 读取文件,可以使用scala.io.Source对象的fromFile方法.如果读取所有行可以使用getLines方法: val source = Source.fromFile(&qu ...

  6. [Scala]Scala学习笔记五 Object

    1. 单例对象 Scala没有静态方法或静态字段,可以使用object来达到这个目的,对象定义了某个类的单个实例: object Account{ private var lastNumber = 0 ...

  7. [Scala]Scala学习笔记四 类

    1. 简单类与无参方法 class Person { var age = 0 // 必须初始化字段 def getAge() = age // 方法默认为公有的 } 备注 在Scala中,类并不声明为 ...

  8. Scala:scala的一些简单操作命令

    Scala是一门多范式的编程语言,一种类似java的编程语言,设计初衷是实现可伸缩的语言.并集成面向对象编程和函数式编程的各种特性. 不太久之前编程语言还可以毫无疑意地归类成“命令式”或者“函数式”或 ...

  9. [Scala]Scala学习笔记三 Map与Tuple

    1. 构造映射 可以使用如下命令构造一个映射: scala> val scores = Map("Alice" -> 90, "Kim" -> ...

  10. [Scala]Scala学习笔记二 数组

    1. 定长数组 如果你需要一个长度不变的数组,可以使用Scala中的Array. val nums = new Array[Int](10) // 10个整数的数组 所有元素初始化为0 val str ...

随机推荐

  1. C# TCP多线程服务器示例

    前言 之前一直很少接触多线程这块.这次项目中刚好用到了网络编程TCP这块,做一个服务端,需要使用到多线程,所以记录下过程.希望可以帮到自己的同时能给别人带来一点点收获- 关于TCP的介绍就不多讲,神马 ...

  2. 移动OA日程支持费用及评论

    业务介绍 AIO7系统最新更新版本在移动OA的日程管理进行改进,增加了创建费用的功能,且在日程批注上也可查看:在日程个人界面和批注界面都支持了评论功能.移动OA上日程对费用及评论的支持,方便用户外出时 ...

  3. H5微场景宽、高度自适应办法

    最近在做一些手机端微场景,发现处理各种手机屏幕分辨率是个很让人头疼的事情,最终找到了一个处理效果比较满意的方案.各位客观请往下看: 如果有过做微场景经历的客官们应该都了解,在代码中给一个元素的宽高设成 ...

  4. win7下使用git

    1 安装git for window 2 安装tortoiseGit 3 生成public key 3.1 打开git bash 3.2 创建~/.ssh文件夹 mkdir ~/.ssh 3.3 配置 ...

  5. HttpServletRequest 各种方法总结

    HttpServletRequest HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,开发人员通过这个对象 ...

  6. Cannot use ImageField because Pillow is not installed.

    现象描述: 使用 ImageField ,创建数据库表时,提示如下: Cannot use ImageField because Pillow is not installed. HINT: Get ...

  7. 使用sudo提示用户不在sudoers文件中的解决方法

    切换到root用户 [linux@localhost ~]$ su root 密码: [root@localhost ~]# 2 查看/etc/sudoers文件权限,如果只读权限,修改为可写权限 [ ...

  8. 【G】系列导航

    G.开源的分布式部署解决方案 [G]开源的分布式部署解决方案 - 预告篇 [G]开源的分布式部署解决方案(一) - 开篇 [G]开源的分布式部署解决方案(二) - 好项目是从烂项目基础上重构出来的 [ ...

  9. 在调用相机后idleTimerDisabled失效的问题

    在调用相机后idleTimerDisabled失效的问题 相关资料: http://stackoverflow.com https://github.com/jamiemcd 问题 前几天有人在群里边 ...

  10. Java基础二:常量池

    目录: 自动装箱与拆箱 常量池 ==与equals()区别 1. 自动装箱与拆箱 Java是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成 ...