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 StarfleetComponentclass Starship extends StarfleetComponent with StarfleetWarpCore

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

class StarfleetComponent
trait StarfleetWarpCore extends StarfleetComponentclass RomulanStuff// won‘t compileclass 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 Starshipclass Enterprise extends Starship with StarfleetWarpCore

  而如下代码则编译错误  

class RomulanShip// this won‘t compileclass Warbird extends RomulanShip with StarfleetWarpCore

  3. 讨论

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

trait WarpCore {    this: Starship with WarpCoreEjector with FireExtinguisher =>}

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

class Starship
trait WarpCoreEjector
trait FireExtinguisher// this worksclass 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 debuggerval child = new Child// debugger added as the object is createdval problemChild = new ProblemChild with Debugger

  2.9 像特质一样继承Java接口

  1. 问题描述

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

  2. 解决方案

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

// javapublic interface Animal {    public void speak();
}public interface Wagging {    public void wag();
}public interface Running {    public void run();
}

  你可以以Scala方式创建Dog类

// scalaclass Dog extends Animal with Wagging with Running {
    def speak { println("Woof") }
    def wag { println("Tail is wagging!") }
    def run { println("I‘m running!") }
}

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

时间: 2024-10-09 02:19:30

Scala之Traits的相关文章

Scala 堆叠Traits实现对方法的增强

Scala 堆叠Traits实现对方法或函数的增强 简单的来说,堆叠Trait类似于AOP,实现对方法功能的增强,而不去修改原有方法的逻辑. 比如我们实现一个队列,我们可以定义一个队列的抽象类,实现入队和出队操作,用scala这样写,和java差不多, abstract class IntQueue {   def get(): Int   def put(x: Int) } 我们去实现这个抽象类, class BasicIntQueue extends IntQueue {   private

Scala学习笔记(2)-- Traits

本文记录我对Scala语言Traits的一些理解. trait >= interface Scala语言没有接口(Interface)的概念,取而代之的,是功能更加强大的Trait.因此,interface在Scala语言里并不是关键字,我们可以自由的使用它,如下面这段代码所示: 但是要注意,上面的代码虽然是合法的Scala代码,能编译出ScalaObject.class.但是如果想在Java里正常使用这个class的话,就会遇到问题. 没有具体方法的Trait会被编译成接口 如果一个Trait

【Scala】Scala之Classes and Properties

一.前言 前面学习了控制结构,下面学习Scala的Class和Properties. 二.Class&Properties 尽管Scala和Java很类似,但是对类的定义.类构造函数.字段可见性控制等则不相同,Java更为冗长,Scala精炼.本章将通过Scala的构造函数工作原理来理解Scala的类和字段,当申明类构造函数参数和使用var.val.private关键字来修饰类的字段时,Scala编译器将会为你生成代码,根据字段修饰符不同,Scala编译器会生成不同的存取函数,本章也会展示如何重

Monad in Scala

Scala有很强的类型系统.加上一些隐式规则,我们可以在scala里模拟haskell的monad. 先从haskell的monad type class开始: class Monad M where ret :: a -> M a bind :: M a -> (b -> M b) -> M b 这里M是type class的参数.它是一个高阶类型, kind是 * –> *.认识到这个很重要,因为如果我们想在scala里面模拟,我们首先需要知道是否scala提供了相应的东

Scala入门到精通——第十节 Scala类层次结构、Traits初步

本节主要内容 Scala类层次结构总览 Scala中原生类型的实现方式解析 Nothing.Null类型解析 Traits简介 Traits几种不同使用方式 1 Scala类层次结构 Scala中的类层次结构图如下: 来源:Programming in Scala 从上面的类层次结构图中可以看到,处于继承层次最顶层的是Any类,它是scala继承的根类,scala中所有的类都是它的子类 Any类中定义了下面几个方法: //==与!=被声明为final,它们不能被子类重写 final def ==

Beginning Scala study note(8) Scala Type System

1. Unified Type System Scala has a unified type system, enclosed by the type Any at the top of the hierarchy and the type Nothing at the bottom of the hierarchy. All Scala types inherit from Any. # Using Any, Book extends AnyRef, and x is an Int that

Scala学习(1)——Scala基础知识

本文要解决的问题: Spark主要是由Scala语言编写而成的,所以要真正深入了解Spark,必须要熟悉Scala,在此结合阅读<Scala编程>这本书的情况,对Scala语言做一个基本的总结. Scala的优势 (1)简洁 类型推断 函数创建的文法支持 (2)Java互操作性 可重用Java库 可重用Java工具 没有性能惩罚 Scala工作机制 编译成Java字节码 可在任何标准JVM上运行,甚至是一些不规范的JVM上 Scala编译器是Java编译器的作者写的 启动解释器 输入Scala

Beginning Scala study note(9) Scala and Java Interoperability

1. Translating Java Classes to Scala Classes Example 1: # a class declaration in Java public class Book{} # Scala equivalent of a class declaration class Book Example 2: # a Java class with a Construtor public class Book{ private final int isbn; priv

Scala中apply的用法

Scala中的 apply 方法有着不同的含义, 对于函数来说该方法意味着调用function本身, 以下说明摘自Programming in Scala, 3rd Edition Every function value is an instance of some class that extends one of several FunctionN traits in package scala, such as Function0 for functions with no parame