抽象属性和抽象方法
基本语法
定义抽象类:使用 abstract 关键字标记,比如 abstract class Person{} ,表明这是一个不能被实例化的类,仅用于被继承并由子类实现其抽象部分。
定义抽象属性:像 val|var name:String ,属性没有初始化值,意味着子类必须提供具体实现。
定义抽象方法:如 def hello():String ,只有声明而无方法体,需子类去实现具体逻辑。
匿名子类
说明
和Java类似,可通过包含定义或重写代码块创建匿名子类,即不单独命名子类,直接在使用父类的地方进行匿名实现。
单例对象语法
基本语法
使用 object 关键字声明单例对象,如 object Person{ val country:String="China" } ,定义了名为 Person 的单例对象并声明了属性 country 。
说明
单例对象用 object 声明 。
若单例对象名与类名一致,该单例对象是对应类(伴生类)的伴生对象,且名称需一致。
单例对象中的属性和方法可通过伴生对象名(类名)直接调用访问。
apply方法
说明
伴生对象的 apply 方法可实现不使用 new 关键字创建对象。
若想将主构造器设为私有,可在 () 前加 private 。
apply 方法可重载。
obj(arg) 语句实际是调用 obj.apply(arg) ,统一面向对象编程和函数式编程风格。
使用 new 关键字构建对象时调用类的构造方法,直接用类名构建对象时调用伴生对象的 apply 方法 。
apply方法
说明
对象创建简化:通过伴生对象的 apply 方法,能不借助 new 创建对象,让代码更简洁,如 Person() 等效于调用伴生对象 Person 的 apply 方法 。
构造器私有化:在构造器前加 private ,可将主构造器设为私有,限制外部直接通过 new 创建对象 。
方法重载: apply 方法支持重载,可定义多个同名但参数不同的 apply 方法,以满足不同创建对象的需求 。
语法糖原理: obj(arg) 本质是 obj.apply(arg) ,统一了编程风格,让函数式和面向对象编程融合更自然 。
构造方式差异: new 调用类构造方法,直接用类名则调用伴生对象的 apply 方法 。
特质(Trait)
特质概念
Scala 中用 trait 替代接口概念,当多个类有相同特征时,可将其独立成 trait 。 trait 可含抽象及具体属性、方法,一个类能混入多个 trait ,类似Java抽象类,是对单继承机制的补充 。
特质声明
基本语法: trait 特质名 {trait主体} 。
基本语法
无父类情况:使用 class 类名 extends 特质1 with 特质2 with 特质3 ... 语法,表明类继承多个特质 。
有父类情况:采用 class 类名 extends 父类 with 特质1 with 特质2 with 特质3 ... ,类在继承父类的同时混入多个特质 。
说明
类与特质关系:类和特质是继承关系。
关键字使用规则:类继承特质时,第一个连接词用 extends ,后续连接特质用 with 。
继承顺序:当类同时继承父类和特质,父类要写在 extends 后面 。
特质方法特性:特质可同时包含抽象方法和具体方法 。
类混入特质数量:一个类能混入多个特质 。
Java接口与Scala特质兼容性:所有Java接口可当作Scala特质使用 。
动态混入
概念:创建对象时可灵活混入 trait ,无需类预先定义混入 。
未实现方法处理:若混入的 trait 有未实现方法,使用类需实现这些方法 。文中还给出了 PersonTrait 特质声明属性示例,以及 Teacher 类继承 PersonTrait 和 java.io.Serializable 特质并实现 say 方法的示例 。
冲突类型
无关联特质冲突:当一个类(Sub)混入两个没有关联的特质(TraitA、TraitB ),且它们有相同的具体方法时,会产生冲突。解决办法是在类(Sub)中重写冲突方法 。
钻石型继承冲突(钻石问题):一个类(Sub)混入的两个特质(TraitA、TraitB )继承自相同的特质(TraitC ),且存在相同具体方法。Scala采用特质叠加策略解决
特质叠加策略
将混入的多个特质中冲突的方法按顺序叠加处理。以代码示例来说:
Ball 特质定义了 describe 方法返回 "ball" 。
Color 特质扩展 Ball 特质,重写 describe 方法,在原方法返回值前加上 "blue-" ,通过 super.describe() 调用父特质的 describe 方法 。
Category 特质同样扩展 Ball 特质,重写 describe 方法并在返回值前加上 "foot-" 。
MyBall 类扩展 Category 特质并混入 Color 特质,重写 describe 方法,通过 super.describe() 依次调用叠加的特质方法,最终按顺序组合方法返回值 。
特质叠加执行顺序
当类混入多个特质,Scala会对特质及其父特质排序。以 MyBall extends Category with Color 为例:
排序步骤:先列出 Category 继承关系作临时顺序;再列出 Color 继承关系并叠加到临时顺序(已出现特质不再重复);最后将类 MyBall 放在叠加顺序最前 。
super 指代: super.describe() 调用的是排序后下一个特质的 describe 方法 。如 MyBall 中 super 指代 Color , Color 中 super 指代 Category , Category 中 super 指代 Ball 。还可通过 super[指定特质].方法名 调用指定特质方法 。
特质自身类型
特质自身类型可实现依赖注入功能
特质和抽象类的区别
扩展性:一个类可扩展多个特质,但只能扩展一个抽象类,特质在多继承场景更灵活 。
构造函数:抽象类可定义带参数构造函数,特质不行,若需构造函数参数,应选抽象类 。
集合
集合简介
集合分类:Scala集合分为序列(Seq)、集(Set)、映射(Map)三大类,都扩展自 Iterable 特质 。
可变与不可变:Scala为多数集合类提供可变和不可变版本,不可变集合在 scala.collection.immutable 包 ,可变集合在 scala.collection.mutable 包 。
特性对比:不可变集合修改会返回新对象,类似Java的 String ;可变集合可直接修改原对象,类似Java的 StringBuilder 。还建议操作时不可变用符号,可变用方法 。
不可变集合继承图
继承关系:展示了不可变集合的继承层次,如 Traversable 是顶层, Iterable 继承自它, Set 、 Map 、 Seq 等又从 Iterable 派生,还有更具体的子类如 HashSet 、 HashMap 等 。
与Java对比
Set 、 Map 在Java中也存在 。
Seq 是Java没有的,Scala的 List 属于 Seq ,和Java的 List 概念不同 。
for 循环中的 1 to 3 属于 IndexedSeq 下的 Range 。
String 属于 IndexedSeq 。
Queue 和 Stack 归属 LinearSeq 。
Scala的 Map 体系有 SortedMap ,支持排序 。
IndexedSeq 和 LinearSeq 区别: IndexedSeq 通过索引查找定位,速度快,如 String 。
可变数组
定义变长数组
使用 ArrayBuffer 定义可变数组,如 val arr01 = ArrayBuffer[Any](3, 2, 5) 。其中 [Any] 表示可存放任意数据类型 , (3, 2, 5) 是初始化的三个元素 ,且需引入 scala.collection.mutable.ArrayBuffer 。
遍历数组:通过 for (i <- arr01) 语法遍历可变数组 arr01 ,可打印数组元素、长度及哈希码 。
增加元素
arr01 += (4) 可追加单个数据 。
arr01.append(5,6) 能向数组最后追加多个数据 。
arr01.insert(0,7,8) 可向指定位置插入数据 。
不可变数组与可变数组的转换
说明
arr1.toBuffer :将不可变数组转换为可变数组 ,原不可变数组 arr1 本身不变 。
arr2.toArray :把可变数组转换为不可变数组 ,原可变数组 arr2 本身不变