scala学习笔记
前言
一、静态属性与静态方法
Java的可以通过static 定义静态属性和静态方法,
public static 返回值类型 方法名( 参数列表) { 方法体}
通过类名.方法名直接调用而不用实例化或者说通过具体对象调用的,所以JAVA的静态操作并不是面向对象的。 Scala语言是完全面向对象( 万物皆对象)的语言,所以没有静态的概念和操作,但是为了能够和Java语言交互,就产生了一种特殊的对象来模拟类对象 ,我们称之为类的伴生对象。所有静态内容都可以放置在它的伴生对象中声明和调用。
//伴生类,编译后生成Dog.class文件
class Dog{
var name : String = _
}
//伴生对象,里面全是静态内容,编译后生成Dog$.class文件
object Dog{
var sex : Boolean = true
def eat(food:String)={
println(name+"like eat "+food)
}
//在伴生对象中定义apply方法,可以实现,类名(参数)方式来创建对象实例
def apply(name:String): Dog = {
println("apply 被调用")
new Dog(name)
}
}
//不用实现类对象,自己使用伴生对象调用方法和属性
println(Dog.name)
println (Dog.eat("food"))
//类名(参数)方式来创建对象实例
val nancy = Dog.apply("nancy.pelosi")
- Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 " 静态" 内容,可以通过 伴生对象名称直接调用。
- 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致。
- 伴生对象中的属性和方法都可以通过伴生对象名( 类名) 直接调用访问
- 从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的
- 伴生对象的声明应该和伴生类的声明在 同一个源码文件中,如果不在同一 个文件中会运行错误。 6.在伴生对象中定义apply方法,可以实现,类名(参数)方式来创建对象实例
二、接口与特质
1.trait定义
Java中的接口
interface 接口名
// 实现接口
class 类名 implements 接口名1
1) 在Java中, 一个类可以实现多个接口。
2) 在Java中,接口之间支持多继承
3) 接口中属性都是常量
4) 接口中的方法都是抽象的
在Scala中,没有接口,而采用特质trait(特征),等价于(interface + abstract class),特质可以同时拥有抽象方法和具体方法(对比:java接口中全是抽象方法),一个类可以实现/继承多个特质。
trait 的声明
trait 特质名(首字母大写) {
trait体
}
在scala中,java中的接口可以当做特质使用
2.trait的使用
一个类具有某种特质(特征),就意味着这个类满足了这个特质(特征)的所 有要素,所以在使用时,也采用了extends关键字,如果有多个特质或存在父 类,那么需要采用with关键字连接
//没有父类
class 类名 extends 特质1 with 特质2 with 特质3 ..
// 有父类
class 类名 extends 父类 with 特质1 with 特质2 with
3.动态混入
- 除了可以在类声明时继承特质以外,还可以在构建对象时 混入特质,扩展目标类的功
- 此种方式也可以应用于对抽象类功能进行扩展
- 动态混入是 是Scala 特有的方式(java 没有动态混入),可在不修改类声明/定义的情况下,扩展类的功能,非常的灵活, 耦合性低 。
- 动态混入可以在不影响原有的继承关系的基础上,给指定的类扩展功能。
trait Operate3 {
def insert(id: Int): Unit = {
println(" 插入数据 = " + id)
}
}
class oracledb {}
abstract class mysqldb{}
//创建对象时拓展特质
var oracle = new oracledb with Operate3
oracle.insert(999)
//抽象类时创建拓展特质
val mysql = new mysqldb with Operate3
mysql.insert(4)
4.叠加特质
同时如果混入多个特质,称之为 叠加特质,那么 特质声明顺序从左到右 ,方法执行顺序 从右到左 注意事项和细节
- 特质声明顺序从左到右。
- Scala在执行叠加对象的方法时,会首先从后面的特质(从右向左)开始执行
- Scala中特质中如果调用super,并不是表示调用父特质的方法,而是 向前 面(左边)继续查找特质,如果找不到,才会去父特质查找**
- 如果想要调用具体特质的方法,可以指定: super[特质].xxx(…).其中的泛型必须是该特质的直接超类类型
trait Operate4 {
println("Operate4...")
def insert(id : Int)
}
trait Data4 extends Operate4 {
println("Data4")
override def insert(id : Int): Unit = {
println("插入数据 = " + id)
}
}
trait DB4 extends Data4 {
println("DB4")
override def insert(id : Int): Unit = {
print("向数据库")
super.insert(id)
}
}
trait File4 extends Data4 {
println("File4")
override def insert(id : Int): Unit = {
print("向文件")
super.insert(id)
//如果想要调**用具体特质的方法,可以指定:
//super[特质].xxx(…).其中的泛型必须是该特质的直接超类类型*
//super[Data4].insert(id)
}}
class MySQL4 {}
def main(args: Array[String]): Unit = {
//特质堆叠顺序 Operate4>Data4>DB4>File4
val mysql = new MySQL4 with DB4 with File4
mysql.insert(888)
// Operate4...
// Data4
// DB4
// File4
// 向文件向数据库插入数据 = 888
//从结果看:先调用了file4,后调DB4,从右向左,或者说从后像前
val mysql = new MySQL4 with File4 with DB4
mysql.insert(999)
// Operate4...
// Data4
// File4
// DB4
// 向数据库向文件插入数据 = 999
//从结果看:先调用了DB4,后调File4,从右向左,或者说从后像前
}
5.在特质中重写抽象方法特例
trait Operate5 {
def insert(id : Int)
}
trait File5 extends Operate5 {
def insert( id : Int ): Unit = {
println("将数据保存到文件中..")
super.insert(id)
}
}
这里会报错,因为super().insert 调用了Operate5的抽象方法。
解决办法:
方式1 : 去掉 super()...
方式2: 调用父特质的抽象方法,那么在实际使用时,没有方法的具体实现,无法编译通过,为了避免这种情况的发生。 可重写抽象方法,这样在使用时,就必须考虑动态混入的顺序问题
abstract override 就是明确的告诉编译器,该方法确实是重写了父特质的抽象方法,但是重写后,该方法仍然是一个抽象方法(因为没有完全的实现, 需要其它特质继续实现[ 通过混入顺序])
trait File5 extends Operate5 {
abstract override def insert( id : Int ): Unit = {
println("将数据保存到文件中..")
super.insert(id)
}
重写抽象方法时需要考虑混入特质的 顺序问题和完整性问题
object traitTest2 {
def main(args: Array[String]): Unit = {
var mysql2 = new MySQL5 with DB5 //ok,实现了抽象方法
mysql2.insert(100)
var mysql3 = new MySQL5 with File5 //error ,还是一个抽象方法,没有完全实现
mysql3.insert(100)
var mysql4 = new MySQL5 with File5 with DB5// error //先执行DB5,后执行Files5,调用super依然是抽象方法
mysql4.insert(100)
var mysql5 = new MySQL5 with DB5 with File5 // ok,没有问题,先调用FILE5,其中super实际是DB5中的insert,所有没有问题
mysql5.insert(100)
}
}
trait Operate5 {
def insert(id : Int)
}
trait File5 extends Operate5 {
abstract override def insert( id : Int ): Unit = {
println("将数据保存到文件中..")
super.insert(id)
}
}
trait DB5 extends Operate5 {
def insert( id : Int ): Unit = {
println(" 将数据保存到数据库中..")
}
}
class MySQL5{}
6.特质构造顺序
第一种:声明类的混入特质
- 调用当前类的超类构造器
- 第一个特质的父特质构造器
- 第一个特质构造器
- 第二个特质构造器的父特质构造器, 如果已经执行过, 就不再执行
- 第二个特质构造器
- .......重复4,5的步骤(如果有第3个,第4个特质)
- 当前类构造器
第二种:在构建对象时,动态混入特质
- 调用当前类的超类构造器 2) 当前类构造器
- 第一个特质构造器的父特质构造器
- 第一个特质构造器.
- 第二个特质构造器的父特质构造器, 如果已经执行过,就不再执行
- 第二个特质构造器
- .......重复5,6的步骤(如果有第3个,第4个特质) 8) 当前类构造器
7.扩展类的特质
特质可以继承类,以用来拓展该类的一些功能,所有混入该特质的类,会自动成为那个特质所继承的超类的子类(间接继承)。
trait MyTraitException extends Exception{
def log(): Unit ={
println(getMessage()) // 方法来自于Exception类
}
}
//MyException 就是Exception 的子类.
class MyException extends MyTraitException{
// 已经是Exception的子类了,所以可以重写方法
override def getMessage = "错误消息!"
}
但是 如果混入该特质的类,已经继承了另一个类(A 类) ,则要求A 类是特质超类的子类,否则就会出现了 多继承现象 ,发生错误
四、嵌套类
略,后续补充