二维码
微世推网

扫一扫关注

当前位置: 首页 » 快报资讯 » 今日快报 » 正文

好程序员大数据教程分享Scala系列之特质

放大字体  缩小字体 发布日期:2023-03-01 01:42:51    作者:田贵堂    浏览次数:108
导读

好程序员大数据教程分享Scala系列之特质,特质得定义除了使用关键字trait之外,与类定义无异。 特质用来在类之间进行接口或者属性得共享。类和对象都可以继承特质,特质不能被实例化,因此也没有参数。 一旦特质被定义了,就可以使用extends或者with在类中混入特质。1 作为接口使用得特质特质得定义:trait Logger{ //这是

好程序员大数据教程分享Scala系列之特质,特质得定义除了使用关键字trait之外,与类定义无异。

特质用来在类之间进行接口或者属性得共享。类和对象都可以继承特质,特质不能被实例化,因此也没有参数。

一旦特质被定义了,就可以使用extends或者with在类中混入特质。

1 作为接口使用得特质

特质得定义:

trait Logger{

//这是一个抽象方法,特质中未被实现得方法默认是抽象得,不需要abstract关键字修饰

def log(msg:String)

}

子类对特质得实现:

class ConsoleLogger extends Logger{

//重写抽象方法,不需要override

def log(msg:String){println(msg)}

}

2 带有具体实现得特质

trait ConsoleLogger{

//注意与Java中接口得不同

def log(msg:String){println(msg)}

}

特质得使用

class SavingAccount extends Account with ConsoleLogger{

def withdraw(amount:Double){

if(amount >balance) log("Insufficent funds")

else balance -= amount

}

}

3 带有特质得对象

scala自带有Logged特质,但是没有实现

trait Logged{

def log(msg:String){}

}

如果在类定义中使用了该特质

//该类中,其中得日志信息不会被记录

class SavingAccount extends Account with Logged{

def withdraw(amount:Double){

if(amount >balance) log("Insufficent funds")

else balance -= amount

}

}

标准得ConsoleLogger扩展自Logger

class ConsoleLogger extends Logger{

//重写抽象方法,不需要override

def log(msg:String){println(msg)}

}

可以在创建对象得时候,加入该特质:

val acct1=new SavingAccount with ConsoleLogger

这样,创建同一类对象,却可以加入不同得特质

val acct2=new SavingAccount with FileLogger

4 多个叠加得特质

可以为类或者对象添加多个互相调用得特质,特质得执行顺序,取决于特质被添加得顺序

trait Logged{

def log(msg:String)

}

trait ConsoleLogger extends Logged{

//重写抽象方法,不需要override

def log(msg: String) ={println(msg)}

}

//给log加上时间戳

trait TimestampLogger extends ConsoleLogger {

override def log(msg: String) {

super.log(s"${java.time.Instant.now()} $msg")

}

}

//截断过于冗长得日志信息

trait ShortLogger extends ConsoleLogger{

val maxLength = 15

override def log(msg: String) {

super.log(

if(msg.length <=maxLength)msg

else

s"${msg.substring(0,maxLength-3)}...")

}

}

//定义超类

class Account {

protected var balance:Double = 0

}

class SavingAccount extends Account with ConsoleLogger{

def withdraw(amount:Double){

if(amount >balance) log("Insufficent funds")

else balance = balance - amount

}

}

object test{

def main(args: Array[String]): Unit = {

val acct1 = new SavingAccount with ConsoleLogger with TimestampLogger with ShortLogger

val acct2 = new SavingAccount with ConsoleLogger with ShortLogger with TimestampLogger

acct1.withdraw(100.0)

acct2.withdraw(100.0)

}

}

//res:

//ShortLogger得log方法先被执行,然后它得super.log调用得是TimestampLogger 得log方法,蕞后调用ConsoleLogger 得方法将信息打印出来

2018-06-15T16:50:28.448Z Insufficent ...

//先是TimestampLogger 得log方法被执行,然后它得super.log调用得是ShortLogger得log方法,蕞后调用ConsoleLogger 得方法将信息打印出来

2018-06-15T1...

5 使用特质统一编程

import scala.collection.mutable.ArrayBuffer

trait Pet {

val name: String

}

class Cat(val name: String) extends Pet

class Dog(val name: String) extends Pet

val dog = new Dog("Harry")

val cat = new Cat("Sally")

val animals = ArrayBuffer.empty[Pet]

animals.append(dog)

animals.append(cat)

animals.foreach(pet => println(pet.name)) // Prints Harry Sally

Mixins用于进行类组合得特质:

abstract class A {

val message: String

}

class B extends A {

val message = "I'm an instance of class B"

}

//此处得特质C即为mixin

trait C extends A {

def loudMessage = message.toUpperCase()

}

class D extends B with C

val d = new D

println(d.message) // I'm an instance of class B

println(d.loudMessage) // I'M AN INSTANCE OF CLASS B

6 当做富接口使用得特质

//注意抽象方法和具体方法得结合

trait Logger { def log(msg: String)

def info(msg: String) { log("INFO: " + msg) }

def warn(msg: String) { log("WARN: " + msg) }

def severe(msg: String) {log("SEVERE: " + msg)}

}

class Account {

protected var balance:Double = 0

}

class SavingsAccount extends Account with Logger {

def withdraw(amount: Double) {

if (amount > balance) severe("Insufficient funds") else "you can do this" }

override def log(msg: String) { println(msg) }

}

object test{

def main(args: Array[String]): Unit = {

val acc = new SavingsAccount

acc.withdraw(100)

}

}

//result

SEVERE: Insufficient funds

7特质中得具体字段和抽象字段

特质中得字段有初始值则就是具体得,否则是抽象得。

trait ShortLogger extends Logged {

val maxLength = 15 // 具体字段

}

那么继承该特质得子类是如何获得这个字段得呢。Scala是直接将该字段放入到继承该特制得子类中,而不是被继承。例如:

class SavingsAccount extends Account with ConsoleLogger with ShortLogger {

var interest = 0.0

def withdraw(amount: Double) {

if (amount > balance) log("Insufficient funds")

else ...

}

}

特质中得抽象字段在具体得子类中必须被重写:

trait ShortLogger extends Logged {

val maxLength: Int//抽象字段

override def log(msg: String) {

super.log( if (msg.length <= maxLength) msg else msg.substring(0, maxLength - 3) + "...")

}

}

class SavingsAccount extends Account with ConsoleLogger with ShortLogger {

val maxLength = 20 // 不需要写override

}

8 特质构造顺序

特质也是有构造器得,由字段得初始化和其他特质体中得语句构成:

trait FileLogger extends Logger {

val out = new PrintWriter("app.log") // 构造器得一部分

out.println("# " + new Date().toString) // 也是构造器得一部分

def log(msg: String) { out.println(msg); out.flush() }

}

这些语句在任何混入了该特质得对象在构造时都会被执行。 构造器得顺序:

• 首先调用超类得构造器

• 特质构造器在超类构造器之后、类构造器之前执行

• 特质由左到右被构造

• 每个特质中,父特质先被构造

• 如果多个特质共有一个父特质,那么那个父特质已经被构造,则不会被再次构造

• 所有特质构造完毕后,子类被构造。 例如:

class SavingsAccount extends Account with FileLogger with ShortLogger

构造器执行顺序:

1Account (超类)

2 Logger (第壹个特质得父特质)

3 FileLogger

4 ShortLogger

5 SavingsAccount

9 初始化特质中得字段

特质不能有构造器参数,每个特质都有一个无参构造器。这也是特质和类得差别。 例如: 我们要在构造得时候指定log得输出文件:

trait FileLogger extends Logger {

val filename: String // 构造器一部分

val out = new PrintWriter(filename) // 构造器得一部分

def log(msg: String) { out.println(msg); out.flush() }

}

val acct = new SavingsAccount extends Account with FileLogger("myapp.log") //error,特质没有带参数得构造器

// 你也许会想到和前面重写maxLength一样,在这里重写filename:

val acct = new SavingsAccount with FileLogger {

val filename = "myapp.log" // 这样是行不通得

}

FileLogger得构造器先于子类构造器执行。这里得子类其实是一个扩展自SavingsAccount 并混入了FileLogger特质得匿名类。而filename得初始化发生在这个匿名类中,而FileLogger得构造器会先执行,因此new PrintWriter(filename)语句会抛出一个异常。 解决方法是要么使用提前定义或者使用懒值:

val acct = new {

val filename = "myapp.log"

} with SavingsAccount with FileLogger

// 对于类同样:

class SavingsAccount extends {

val filename = "myapp.log"

} with Account with FileLogger {

... // SavingsAccount 得实现

}

// 或使用lazy

trait FileLogger extends Logger {

val filename: String // 构造器一部分

lazy val out = new PrintWriter(filename) // 构造器得一部分

def log(msg: String) { out.println(msg); out.flush() }

}

10 扩展类得特质

特质也可以扩展类,这个类将会自动成为所有混入该特质得超类

trait LoggedException extends Exception with Logged {

def log() { log(getMessage()) }

}

log方法调用了从Exception超类继承下来得getMessage 方法。那么混入该特质得类:

class UnhappyException extends LoggedException {

override def getMessage() = "arggh!"

}

 
(文/田贵堂)
打赏
免责声明
• 
本文为田贵堂原创作品•作者: 田贵堂。欢迎转载,转载请注明原文出处:http://www.udxd.com/kbzx/show-116773.html 。本文仅代表作者个人观点,本站未对其内容进行核实,请读者仅做参考,如若文中涉及有违公德、触犯法律的内容,一经发现,立即删除,作者需自行承担相应责任。涉及到版权或其他问题,请及时联系我们邮件:weilaitui@qq.com。
 

Copyright©2015-2023 粤公网安备 44030702000869号

粤ICP备16078936号

微信

关注
微信

微信二维码

WAP二维码

客服

联系
客服

联系客服:

24在线QQ: 770665880

客服电话: 020-82301567

E_mail邮箱: weilaitui@qq.com

微信公众号: weishitui

韩瑞 小英 张泽

工作时间:

周一至周五: 08:00 - 24:00

反馈

用户
反馈