scala 学习笔记(05) OOP(中)灵活的trait
trait -- 不仅仅只是接口!
接上回继续,scala是一个非常有想法的语言,从接口的设计上就可以发现它的与众不同。scala中与java的接口最接近的概念是trait,见下面的代码:
package yjmyzz
object App {
def main(args: Array[String]) {
val bird = Bird("pigeon")
bird.fly
println(bird.isInstanceOf[Bird]) //true
println(bird.isInstanceOf[Flyable]) //true
println(bird.toString) //this is a Bird:pigeon
bird.test //hello
}
}
/**
* 定义一个"接口"
*/
trait Flyable {
/**
* 定义接口方法
*/
def fly;
/**
* 接口中也有可以方法的实现(是不是惊呆了!)
* @return
*/
def test = {
println("hello")
}
}
class Bird(var name: String) extends Flyable {
/**
* 实现接口中的方法
*/
def fly: Unit = {
println("I am a " + name + ", and I can fly~")
}
override def toString = {
"this is a Bird:" + name
}
}
object Bird {
def apply(name: String) = {
new Bird(name)
}
}
从上面的代码中,可以看出trait与java中interface的异同,相同的是如果把trait单纯当接口来用,trait中只需要定义方法签名即可,然后由"子类"来实现。不同的是,scala中的trait里也可以有方法实现!而且实现接口时,关键字不是implements而是extends(当然,还可能是with,后面还会提到),这说明scala中trait并不仅仅只是接口,它也是一种特殊的类。
trait的mixin:
trait还有一个神奇的特性,可以在运行时,动态与其它类合体!见下面的代码:
package yjmyzz
object App {
def main(args: Array[String]) {
val duck = new Bird("duck") with Swim
println(duck.isInstanceOf[Flyable]) //true
println(duck.isInstanceOf[Swim]) //true 注意这里:表示此时Bird也是一个Swim实例
duck.fly //I am a duck, and I can fly~
duck.swim //I can swim! 注:是不是很有意思,绝对的动态晚绑定!
}
}
/**
* 定义一个"接口"
*/
trait Flyable {
/**
* 定义接口方法
*/
def fly;
/**
* 接口中也有可以方法的实现(是不是惊呆了!)
* @return
*/
def test = {
println("hello")
}
}
/**
* 再来一个"接口"
*/
trait Swim {
def swim = {
println("I can swim!")
}
}
class Bird(var name: String) extends Flyable {
/**
* 实现接口中的方法
*/
def fly: Unit = {
println("I am a " + name + ", and I can fly~")
}
override def toString = {
"this is a Bird:" + name
}
}
我们又新增了一个trait:Swim,然后注意第7行,通过with Swim,硬是把跟Swim毫不相关的Bird实例跟它搅在一起了,然后实例duck就获得了Swim的能力! 这种场景下,trait就不应该理解为接口,而应该认为它是一种特性,是一种可以动态赋予其它实例的超能力!(这也是为什么关键字不叫interface,而是叫trait的原因吧)
trait与java中的接口还有一个明显的区别,trait可以继承自类,java中的interface可是不允许继承自class的! 见下面的代码示例:
package yjmyzz
/**
* 动物基类
*/
class Animal {}
/**
* 会飞的动物("接口"继承类)
*/
trait FlyAnimal extends Animal {
def fly;
}
/**
* 即会飞又会游泳的动物("接口"继承"接口")
*/
trait SwimAndFlyAnimal extends FlyAnimal {
def swim;
}
/**
* 会说话(注:这个接口是完全独立的,不继承自任何其它类或trait)
*/
trait Talk {
def talk;
}
/**
* 鸟(继承类,又实现"接口",实际上是多重继承)
*/
class Bird(var birdName: String) extends Animal with FlyAnimal {
override def fly: Unit = {
println(birdName + "能飞!")
}
}
/**
* 被继承的class[Animal]与trait[Talk]相互之间也可以没半毛钱关系
*/
class AlienAnimal extends Animal with Talk {
override def talk: Unit = println("外星动物很厉害的啦,它们会说话!")
}
/**
* 类也可以直接继承自trait
*/
class TalkThing extends Talk {
override def talk: Unit = println("我也不知道我是啥,反正我会说话!")
}
object ScalaApp {
def main(args: Array[String]) {
var eagle = new Bird("老鹰")
eagle.fly
println
var swan = new Bird("天鹅") with SwimAndFlyAnimal {
override def swim: Unit = println("天鹅还能游泳!")
}
swan.fly
swan.swim
println
var sparrow = new Bird("麻雀") with Talk {
override def talk: Unit = println {
"麻雀'说话',就是叽叽喳喳的叫!"
}
}
sparrow.fly
sparrow.talk
println
var alienAnimal = new AlienAnimal
alienAnimal.talk
println
var talkThing = new TalkThing
talkThing.talk
}
}
运行结果:
老鹰能飞! 天鹅能飞! 天鹅还能游泳! 麻雀能飞! 麻雀'说话',就是叽叽喳喳的叫! 外星动物很厉害的啦,它们会说话! 我也不知道我是啥,反正我会说话!
关于trait,小结一下:
1、trait"类似"(注:仅仅只是类似)java中的接口,可以只定义方法签名,交由子类去实现
2、trait中也可以有具体的方法实现
3、trait可以继承自trait,也可以继承自class
4、class也可以直接继承自trait
5、trailt可以在运行时,通过with关键,动态混入class实例
- memlock过低导致的数据库性能问题(r6笔记第10天)
- OpenCV和SVM分类器在自动驾驶中的车辆检测
- 自动驾驶的模型预测控制
- 【专业技术】使用html5的十大原因
- 第五课:推理结果的可视化
- 第四课:模型的使用
- 【Java概念学习】--数组的初始化
- linux下重命名文件或文件夹使用mv既可实现。
- 第三课:把tensorflow,模型和测试数据导入Android工程
- D-Link 路由器信息泄露和远程命令执行漏洞分析及全球数据分析报告
- Wordpress安全架构分析
- CVE-2017-5123 漏洞利用全攻略
- 简单分析shared pool(三) (r5笔记第94天)
- OpenCV在车道线查找中的使用
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 给卷积神经网络动动刀:加法网络探究
- 用Python网络爬虫来看看最近电影院都有哪些上映的电影
- 使用 Azure Blob Stoage 实现一个静态文件服务器
- java学习--反射详解
- Redis中的管道Pipeline操作
- 查看YARN任务日志的几种方式
- 简易数字频率计(verilog HDL设计)(2020维护版本)
- C++核心准则CP.111:如果真的需要好双重检查锁,使用惯用模式
- C++核心准则CP.200:使用volatile只能表明该变量是非C++内存
- C++核心准则E.2:通过抛出异常来表明函数无法执行指定的任务
- C++核心准则E.3:异常应该只用于错误处理
- [Go] GO语言实战-gin框架项目实现中英文切换
- [Go] Go语言实战-基于websocket浏览器通知的实现
- 快10年的老前端了,我还分不清 slice 和 splice,这到底是谁的锅
- 第4章 Jenkins系统用户认证配置管理