一、介绍

  参考http://luchunli.blog.51cto.com/2368057/1705025

  我们知道,如果几个类有某些共通的方法或者字段,那么从它们多重继承时,就会出现麻烦。所以Java被设计成 不支持多重继承,但可以实现任意多的接口。接口只能包含抽象方法,不能包含字段。

  特质 trait 是Scala中特有的一项特点,不同于C#与Java,如果一定要拿C#与Java中的某项特点作对比的话,最接近的应该是接口,但是C#与Java中的接口是不允许带有方法实现的,而Scala中的Trait是可以带有方法实现的。这样做的好处需要某个trait就拿来用,而不需要重复实现接口

  所有的Java接口都可以作为Scala特质来使用。与Java一样,Scala类只能有一个超类,可以有任意数量的特质。

  特质的定义使用保留字trait,具体语法与类定义类似,除了不能拥有构造参数

 trait reset {
def reset (m : Int, n : Int) = if (m >= n) 1;
}

  一旦特质被定义了,就可以混入到类中

 class week extends reset {......}

  当要混入多个特质时,利用with保留字

 class week extends reset with B with C {......}

  特质的成员可以是抽象的,而且不需要使用abstract声明。同样,重写特质的抽象方法无需给出override。但是,多个特质重写同一个特质的抽象方法需要给出override。
  除了在类定义中混入特质外,还可以在特质定义中混入特质

 trait reseting extends reset {......}

  在对象构造时混入特质

 val file = new month with reseting

  特质的构造是有顺序的,从左到右被构造

 /**
* @author lucl
*/
class Teacher { // 实验用的空类
println("===============I'm Teacher.");
} trait TTeacher extends Teacher {
println("===============I'm TTeacher.")
def teach; // 虚方法,没有实现
} trait TPianoTeacher extends Teacher {
println("===============I'm TPianoTeacher.")
def playPiano = { // 实方法,已实现
println("I'm playing piano.");
}
} class PianoPlayingTeacher extends Teacher with TTeacher with TPianoTeacher {
println("===============I'm PianoPlayingTeacher.")
def teach = { // 定义虚方法的实现
println("I'm teaching students.");
}
} object TraitOps {
def main (args : Array[String]) {
var p = new PianoPlayingTeacher;
p.teach;
p.playPiano;
}
} /**
===============I'm Teacher.
===============I'm TTeacher.
===============I'm TPianoTeacher.
===============I'm PianoPlayingTeacher.
I'm teaching students.
I'm playing piano.
*/

二、例子

  我们的例子中定义了一个抽象类Aminal表示所有的动物,然后定义了两个trait Flyable和Swimable分别表示会飞和会游泳两种特征。

  Aminmal的实现(定义了walk方法,实现了breathe方法)

 abstract class Animal {
def walk(speed : Int); def breathe () = {
println("animal breathes.");
}
}

  Flyable和Swimable两个 trait的实现

 /**
* @author lucl
* 有两个方法,一个抽象方法一个已实现方法
*/
trait Flyable {
def hasFather = true;
def fly;
} package com.mtrait /**
* @author lucl
* 只有一个抽象方法
*/
trait Swimable {
def swim;
}

  我们定义一种动物,它既会飞也会游泳,这种动物是鱼鹰 FishEagle

 /**
* @author lucl
*/
class FishEagle extends Animal with Flyable with Swimable {
/**
* 实现抽象类的walk方法
*/
override def walk(speed: Int) = {
println ("Fish eagle walk with speed : " + speed + ".");
} /**
* 实现trait Flyable的方法
*/
override def fly = {
println("Fish eagle fly fast.");
} /**
* 实现trait Swimable的方法
*/
override def swim {
println("Fish eagle swim fast.");
}
} object FishEagle {
def main (args : Array[String]) {
val fish = new FishEagle;
fish.walk(100);
fish.fly;
fish.swim;
println("fish eagle has father ? " + fish.hasFather + ".");
// println(fish.swim); // 输出为() println();
val flyable : Flyable = fish;
flyable.fly; val swimable : Swimable = fish;
swimable.swim;
}
} /**
输出结果:
Fish eagle walk with speed : 100.
Fish eagle fly fast.
Fish eagle swim fast.
fish eagle has father ? true. Fish eagle fly fast.
Fish eagle swim fast.
*/

  trait很强大,抽象类能做的事情,trait都可以做,它的长处在于可以多继承。

  trait和抽象类的区别在于抽象类是对一个继承链的,类和类之前确实有父子类的继承关系,而trait则如其名字,表示一种特征,可以多继承。

  在对象中混入trait

/**
* 单独的日志模块
* 只是标识要记录日志,但没有明确定义如何记录日志
*/
trait Logger {
def log (msg : String) {}
} /**
* 记录日志的具体实现类
*/
trait WriteLogger extends Logger {
override def log (msg : String) = {println("WriteLogger : " + msg);}
} /**
* 需要执行的业务操作
*/
trait Action {
def doAction(action : String);
} class TraitActionImpl extends Action {
override def doAction(op : String) = println(op);
} class LoggerActionImpl extends Action with Logger {
override def doAction(op : String) = {
println(op);
// 如果确实需要日志功能但暂不清楚以何种形式记录日志时,可以采用该方法;
// 当明确了记录日志的方式后,再通过如下在对象中混入trait实现。
log (op);
}
} /**
* @author lucl
*/
object TraitOps {
def main (args : Array[String]) {
//
println("===================aaaaaa========================");
// 类本身与记录日志Logger没有关系,但是在对象中混入trait的代码后,就具备了日志的功能
val actionA = new TraitActionImpl with WriteLogger;
val op = "业务操作";
actionA.doAction(op);
actionA.log(op); //
println("===================bbbbbb========================");
// 类实现了Logger,但日志记录是空的操作
val loggerA = new LoggerActionImpl;
loggerA.doAction(op); println("===================cccccc========================");
// 类实现了Logger,通过在类定义中混入trait实现了自己的记日志的功能
val loggerB = new LoggerActionImpl with WriteLogger;
loggerB.doAction(op);
}
} /**
输出结果:
===================aaaaaa========================
业务操作
WriteLogger : 业务操作
===================bbbbbb========================
业务操作
===================cccccc========================
业务操作
WriteLogger : 业务操作
*/

三、总结

  简单来说,Scala的trait就是类似于Java的接口。使一个类能实现多种功能。

Scala入门2(特质与叠加在一起的特质)的更多相关文章

  1. Scala入门学习笔记三--数组使用

    前言 本篇主要讲Scala的Array.BufferArray.List,更多教程请参考:Scala教程 本篇知识点概括 若长度固定则使用Array,若长度可能有 变化则使用ArrayBuffer 提 ...

  2. margin叠加相邻两个元素的上下margin是叠加在一起

    <!doctype html><html><head><meta charset="utf-8"><title>无标题文 ...

  3. Scala入门 【1】

    Scala入门 [1] 转载请注明出处:http://www.cnblogs.com/BYRans/ 1 基础 val定义的为常量,var为变量 val name:Type = ***,变量名后加冒号 ...

  4. Scala入门(1)Linux下Scala(2.12.1)安装

    Scala入门(1)Linux下Scala(2.12.1)安装 一.文件准备 1.1 文件名称 scala-2.12.1.tgz 1.2 下载地址 http://www.scala-lang.org/ ...

  5. scala 入门Eclipse环境搭建

    scala 入门Eclipse环境搭建及第一个入门经典程序HelloWorld IDE选择并下载: scala for eclipse 下载: http://scala-ide.org/downloa ...

  6. Scala 入门详解

    Scala 入门详解 基本语法 Scala 与 Java 的最大区别是:Scala 语句末尾的分号 ; 是可选的 Scala 程序是对象的集合,通过调用彼此的方法来实现消息传递.类,对象,方法,实例变 ...

  7. Scala学习之路 (六)Scala的类、对象、继承、特质

    一.类 1.类的定义 scala语言中没有static成员存在,但是scala允许以某种方式去使用static成员这个就是伴生机制,所谓伴生,就是在语言层面上,把static成员和非static成员用 ...

  8. idea创建Scala入门HelloWorld

    Scala开发环境的搭建 首先到Scala官网下载Scala网址为 https://www.scala-lang.org/download/ 找到下图所示位置:选择相对应的版本的Scala进行下载,这 ...

  9. IntelliJ中的Scala入门

    IntelliJ IDE中的Scala入门 创建项目 打开IntelliJ并单击File => New => Project 在左侧面板中,选择Scala.在右侧面板中,选择IDEA. 将 ...

随机推荐

  1. [Luogu 2169] 正则表达式

    [Luogu 2169] 正则表达式 感谢 0xis 推题. 记忆中很久没有过一遍写过一题了- 别被题目名称蒙骗!这不是正则表达式题目!和字符(串)处理一点关系都没有!这是个图论题啊喂! 题都没急,C ...

  2. 让PHPCms内容页支持JavaScript的修改方法

    在文件..\caches\caches_model\caches_data\content_input.class.php中找到函数: function get($data,$isimport = 0 ...

  3. TensorFlow在windows10上的安装与使用(一)

    随着近两年tensorflow越来越火,在一台新win10系统上装tensorflow并记录安装过程.华硕最近的 Geforce 940mx的机子. TensorFlow是一个采用数据流图(data ...

  4. TIP协议

    1. TIP是什么? CISCO给TIP的定义如下: The TIP protocol specifications describe how to multiplex multiple screen ...

  5. (转载) 天梯赛 L2-018. 多项式A除以B

    题目链接 题目描述 这仍然是一道关于A/B的题,只不过A和B都换成了多项式.你需要计算两个多项式相除的商Q和余R,其中R的阶数必须小于B的阶数. 输入格式: 输入分两行,每行给出一个非零多项式,先给出 ...

  6. [转]CNN 中千奇百怪的卷积方式大汇总

    https://www.leiphone.com/news/201709/AzBc9Sg44fs57hyY.html 推荐另一篇很好的总结:变形卷积核.可分离卷积?卷积神经网络中十大拍案叫绝的操作. ...

  7. 使用qt写的进制转换器

    没有使用什么数据结构,直接使用qt自带的进制转换函数, 实时出结果,代码在后面的链接中,由于初学qt,好多不会,代码构造就有点乱 截图如下

  8. sqlite3 的insert记录项思路

    sqlite3 的insert记录项思路 1.组合一个insert的sql语句 2.判断是否需要立即执行,若不是立刻执行的语句,则插入到待处理的链表中,供后续事务处理时提交.必须有一个专门线程来对事务 ...

  9. spark和hadoop比较

    来源知乎 计算模型:hadoop-MapReduce,Spark-DAG(有向无环图)评注:经常有人说Spark就是内存版的MapReduce,实际上不是的.Spark使用的DAG计算模型可以有效的减 ...

  10. No.1 selenium学习之路之浏览器操作

    selenium基础,首先就是浏览器的相关操作 下面描述几种浏览器的常用操作 1.打开浏览器 webdriver后面添加想要打开的浏览器 Ie或者Chrome 2.打开指定页面(百度) 3.休眠时间 ...