一、介绍

  参考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. Understanding the Bias-Variance Tradeoff

    Understanding the Bias-Variance Tradeoff When we discuss prediction models, prediction errors can be ...

  2. 用代码从文件中导入数据到SQL Server

    引言 导入数据到SQL Server 是常见的需求,特别是定期导入这种需求. 对于定期导入主要有以下几种方式可选择: Bulk Insert Bcp Utility OpenRowSet 写程序导入( ...

  3. 微服务深入浅出(6)-- 熔断器Hystrix

    概念 在分布式系统中,一种不可避免的情况就是某些服务会出现故障,导致依赖他们的其他服务出现远程调度的线程问题(雪崩效应).而Hystrix提供的熔断器,通过隔离服务的访问点,能阻止这种分布式系统中出现 ...

  4. centos7.2 安装 nginx

    Centos 7 源码编译安装 Nginx 1.13 原文地址:https://renwole.com/archives/39 1.先决条件: centos7.2 64位,安装配置nginx前必须安装 ...

  5. 【技巧总结】Penetration Test Engineer[5]-Operating System Security(SQL Server、MySQL提权)

    4.数据库安全基础 4.1.MSSQL 数据库角色权限 sysadmin:执行SQL Server中的任何动作 db_owner:可以执行数据库中技术所有动作的用户 public:数据库的每个合法用户 ...

  6. Django 1.10中文文档-模型参考

    模型字段 本文档包含了Django提供的全部模型 Field 包括 字段选项 和 字段类型 的API参考. 参见 如果内建的字段不能满足你的需求, 你可以蚕食 django-localflavor ( ...

  7. linux通配符,grep和 egrep区别

    其实主要是正则表达式中的一些特殊语法.在网上找的几篇文章,截取相关部分贴在了下面,方便以后翻阅. 参考:http://hi.baidu.com/sei_zhouyu/item/c18e1a950d2e ...

  8. 双机/RAC/Dataguard的区别【转】

    本文转自 双机/RAC/Dataguard的区别-jasoname-ITPUB博客 http://blog.itpub.net/22741583/viewspace-684261/ Data Guar ...

  9. 02 How to Write Go Code 如何编写go语言代码

    How to Write Go Code   如何编写go语言代码 Introduction   介绍 Code organization  组织代码 Overview  概述 Workspaces  ...

  10. Python字符串(Str)详解

    字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串. 创建字符串很简单,只要为变量分配一个值即可 字符串的格式 b = "hello itcast. ...