【Scala谜题】继承
笔记来源:Scala谜题
多级继承
Scala 支持面向对象的编程概念,继承是它的一个很重要的特征。继承通常对父类和特质中定义的缺省值的重载很有用。当增加多级继承时事情变得更加有趣,例如下面这段程序。
trait A {val foo: Intval bar = 10println("In A: foo: " + foo + ", bar: " + bar)
}
class B extends A {val foo: Int = 25println("In B: foo: " + foo + ", bar: " + bar)
}
class C extends B {override val bar = 99println("In C: foo: " + foo + ", bar: " + bar)
}
new C()
它的打印结果我一开始也没想到:
In A:foo:0,bar:0
In B:foo:25,bar:0
In C:foo:25,bar:99
首先我们要知道 val
的初始化和重载规则:
- 超类会在子类之前初始化;
- 按照声明的顺序对成员初始化;
- 当一个
val
被重载时,只能初始化一次; - 与抽象
val
类似,重载的val
在超类构造期间会有一个缺省的初始化。
因此,根据以上规则,在最开始的那段程序中,虽然表面上看在特质 A
中给 bar
分配了一个初始值,实际上却不是这样,因为类 C
中重载了 bar
。这意味着特质 A
构造时,给bar
分配了一个缺省初始值
0,而不是原有的值 10。
Scala 从Java 中继承了初始化顺序规则。Java 确保首先初始化超类,这样就可以从子类构造器中安全使用超类字段,确保正确地初始化字段。特质会被编译成接口和具体的(非抽象的)类,所以也可应用同样的规则。
Scala 给记录赋的缺省初始值为:
- Byte、Short 和Int 类型val 的初始值是0
- Int、Long、Float 和Double 类型val 的初始值分别是0L、0.0f 和0.0d
- Char 类型val 的初始值是'0'
- Boolean 类型val 的初始值是false
- Unit 的初始值是()
- 所有其他类型的初始值是 null
那如果我们想要展示的是这样的结果:
In A:foo:0,bar:99
In B:foo:25,bar:99
In C:foo:25,bar:99
我们应该怎么做呢:
用定义
一种方法是将 bar
声明为 def
,而不是 val
。之所以这个方法能解决问题,是因为 def
这个方法体不属于主构造器,因此不参与类初始化。此外,因为类 C
中重载了 bar
,多态会特别选择使用这个重载的定义。因此,3 个 println
语句中的 bar
都会调用类 C
中的重载定义。
trait A {val foo: Intdef bar: Int = 10println("In A: foo: " + foo + ", bar: " + bar)
}
class B extends A {val foo: Int = 25println("In B: foo: " + foo + ", bar: " + bar)
}
class C extends B {override def bar: Int = 99println("In C: foo: " + foo + ", bar: " + bar)
}
这种方法的一个缺点是每次调用都要评估。Scala 也遵从统一访问原则,所以在超类中定义一个参数方法不会阻止在子类中将它重载为一个 val
,这会导致令人迷惑的行为再次出现,从而破坏原有的架构规划。
lazy val
另外一种避免这种意外的方法是将 bar
声明为 lazy val
。lazy val
在初次访问时初始化。而常规的 val
,又叫静态变量,是在定义时初始化的。lazy val
使用编译器生成的方式初始化,这里将调用特质 C
中 bar
的重载版本。注意,lazy val
的特点是将高成本的初始化过程尽可能推迟到最后时刻(有时可能永远也不进行初始化)。
trait A {val foo: Intlazy val bar = 10println("In A: foo: " + foo + ", bar: " + bar)
}
class B extends A {val foo: Int = 25println("In B: foo: " + foo + ", bar: " + bar)
}
class C extends B {override lazy val bar = 99println("In C: foo: " + foo + ", bar: " + bar)
}
不过,要注意的是,lazy val
也有一些缺点:
- 由于在底层发生同步,这会引起轻微的性能成本;
- 不能声明抽象
lazy val
; - 使用
lazy val
容易产生循环引用,从而导致首次访问时发生栈溢出错误,甚至可能发生死锁; - 如果在对象间做了声明而
lazy val
间的循环依赖却不存时,就可能会发生死锁,这种情况也许非常微妙,不易觉察。
预初始化字段
使用预初始化字段(也就是大家所知道的早期初始化器)也可以达到相同的效果:
trait A {val foo: Intval bar = 10println("In A: foo: " + foo + ", bar: " + bar)
}
class B extends A {val foo: Int = 25println("In B: foo: " + foo + ", bar: " + bar)
}
class C extends {override val bar = 99
} with B {println("In C: foo: " + foo + ", bar: " + bar)
}
这段程序与原来的程序的唯一差别,就是 bar
在类 C
的早期字段定义从句中初始化。早期字段定义从句紧跟着 extends
关键字后的大括号,它是子类的一部分,在超类构造器之前运行。这样就可以确保 bar
在特质 A
被构造之前即被初始化。
总结
用什么方法解决潜在的初始化顺序问题,是因不同的用例而有所不同的:
- 如果每次访问评估表达式的成本不是太高,也许会用定义的方法。
- 或者只要能避免循环依赖,就可以用
lazy val
的方法,这对用户的类来说也许是最简单的解决方案。 - 或者,如果用户很清楚他们应该使用早期字段定义,那么简单地使用原来的抽象
val
也是一个不错的选择。
【Scala谜题】继承相关推荐
- Scala的继承和多态
基本语法 class 子类名 extends 父类名 { 类体} 其中: (1)子类继承父类的属性和方法 (2)scala 是单继承 案例实操 (1)子类继承父类的属性和方法 (2)继承的调用顺序:父 ...
- Scala 多继承问题
多继承问题: object LoadIssueDemo extends App {import java.io.PrintWritertrait Logger {def log(msg: String ...
- 大数据必学语言Scala(三十):scala面向对象 继承(extends)
文章目录 继承(extends) 简单继承 override和super isInstanceOf和asInstanceOf getClass和classOf 访问修饰符
- Scala学习教程笔记二之函数式编程、Object对象、伴生对象、继承、Trait、
1:Scala之函数式编程学习笔记: 1:Scala函数式编程学习:1.1:Scala定义一个简单的类,包含field以及方法,创建类的对象,并且调用其方法:class User {private v ...
- Scala入门到精通——第九节 继承与组合
主要内容 类的继承 构造函数执行顺序 方法重写 匿名类 多态与动态绑定 组合与继承的使用 1 类的继承 下类的代码演示了Scala类的继承 //Person类 class Person(name:St ...
- Scala 继承和特质
一.继承 1.1 Scala中的继承结构 Scala 中继承关系如下图: Any 是整个继承关系的根节点: AnyRef 包含 Scala Classes 和 Java Classes,等价于 Jav ...
- spark之scala快速入门
scala和java都是在jvm之上的语言,相对来讲,scala热度比较低,其实并不是一个特别好的语言选择. 原因倒不是因为scala本身的缺点,而是使用人群不够多,论坛和社区不够活跃.这就跟社交软件 ...
- Scala学习(八)练习
Scala中继承&练习 1. 扩展如下的BankAccount类,新类CheckingAccount对每次存款和取款都收取1美元的手续费 class BankAccount ( initial ...
- Scala基础教程(七):类和对象、特征
扩展一个类: 可以扩展scala类以类似的方式,如在Java中的一样,但有两个限制:方法重载需要override关键字,只有主构造可以传递参数给基构造.现在扩展上面的类,并增加一个类的方法: clas ...
最新文章
- 微信小游戏开发教程-游戏实现1
- 数学奥赛用不用计算机,报考自招必看!五大学科竞赛利弊详解,到底哪科最适合你?...
- SAP Fiori Launchpad的后台配置路径
- 微软人工智能愿景:根植于研发 寄望于“对话”
- 阿里云助力浙江大学信息化建设,以实时数据驱动校园智能管理
- 又一个半成品库 weblog rpc client
- python get函数 i_Python高阶技巧,你 GET了吗?
- ES6--Reflect
- G 音乐鉴赏(非二分解法)
- 若依设置匿名访问路径
- 数据库与身份认证(学习笔记)
- 千锋深圳Java培训分享:MySQL详细知识点
- 零基础想要做好人物角色模型,先了解人体的构造!快来康康
- python求单词长度_python 统计单词平均长度,统计a出现的次数
- 有一个已经排好序的数组,要求输入一个数后,按原来排序的规律将它插入数组中
- 王者荣耀与英雄联盟:如何解决玩家骂人的问题?
- linux 查看dns进程,探查Linux系统DNS服务器运行状况
- 递归解决常见爬楼梯走一步或是两步问题,走多步也是相同的道理!
- Node-RED中使用JSON数据建立web网站
- 推荐系统的评价指标笔记(NDCG、MAP、AUC、HR、MRR)
热门文章
- C语言程序设计 C语言中的时间函数
- 局域网内时间同步的一种简单办法
- blob转成json js_javascript – 文件API – Blob到JSON
- 大学文科计算机考试大纲,(文科)大学计算机信息技术课程考核大纲(文科)介绍.doc...
- 故障码123401_电力系统规划设计对电力工程设计的应用
- mysql+表复制+效率_MySQL数据库复制表的几种方式讲解
- jdbc map获取keys_跟我学shardingjdbc之分布式主键及其自定义
- systemd管理mysql多实例_使用 systemd 配置多个 MySQL 8.0 实例
- docker可视化管理界面_分析一款Docker容器可视化管理工具Porttainer
- 前端HTML5CSS动画变形动画之过渡