面向对象编程高级部分,主要学习Scala中一下四个知识点
一、静态概念
1.单例对象
- Scala比Java 更具有面向对象的特点,Scala的类不允许有静态( static )成员。
- Scala虽然没有静态方法或者静态字段。对此类使用场景, Scala提供Object语法(单例对象)来达到相同的目的。
- 单例对象(singleton object) de 定义看上去和类相似,使用object 关键字替换class 。单例对象定义了某个类的单例对象,其中包含了你想要的特性,例如
//定义一个单例对象。 Object Account{ private var LastNumber = 0 def newUniqeNumber() = { lastNumber += 1 lastNumber } } //调用单例对象中的方法 Account.newUniqeNumber() //创建一个唯一的新的账号。
- 单例对象本质上可以拥有类的所有特性,除了不能提供构造器参数。
- 对于任何在java中使用单例对象的地方,Scala也都可以使使用单例对象实现:
- 作为存放工具函数或者常量的地方。
- 高效的共享单个不可变实例。
- 需要使用某个单例实例来协调某个服务时(参考单例模式)。
2.伴生对象
当单例对象跟某个类共用一个名字时,它被称作这个类的伴生对象(companion object)。
- 必须在同一个源码文件中定义类和类的伴生对象,同时类叫做这个单例对象的伴生类。
- 伴生类和伴生对象可以互相访问对方的私有成员。伴生类可以接受参数,而伴生对象不可以,因为你没法用new实例化单例对象,也就没有任何手段向它传参
- 每个单例对象都是通过一个静态变量引用合成类的实例来实现的,因此单例对象的从初始化的语义上根Java的静态成员是一致的,尤其体现在单例对象在有代码首次访问时才被初始化。
java反编译的代码(在线反编译http://www.javadecompilers.com/):class Account{//伴生类 val id = Account.newUniqueNumber() private var balance = 0.0 def deposit(amount: Double) = { balance += amount} } object Account{//伴生对象 private var lastNumber = 0 def newUniquieNumber() = { lastNumber += 1 lastNumber } } 注意:伴生对象的成员 并不在伴生类的作用域内。Account 类必须通过类名.方法名() 方式调用 例如:应该是Account.newUniqueNumber()调用,而不是直接newUniqueNumber()来调用伴生对象的方法。
class Account半生类 反编译后底层对应Acouunt类 和Acouunt.class源文件
object Account伴生对象反编译后对应 Accountpublic class Account { private final int id; private double balance; public static int newUniqueNumber() { return Account$.MODULE$.newUniqueNumber();//重点关注 } public int id() { return this.id; } private double balance() { return this.balance; } private void balance_$eq(final double x$1) { this.balance = x$1; } public void deposit(final double amount) { this.balance_$eq(this.balance() + amount); } public Account() { this.id = Account$.MODULE$.newUniqueNumber(); this.balance = 0.0; } }
$
类“和 Account$
.class源文件
从源码不难看出,伴生类中val id = Account.newUniqueNumber() 这一行代码,实际上是分成了三步:public final class Account$ { public static Account$ MODULE$; private int lastNumber; static { new Account$(); } private int lastNumber() { return this.lastNumber; } private void lastNumber_$eq(final int x$1) { this.lastNumber = x$1; } public int newUniqueNumber() { this.lastNumber_$eq(this.lastNumber() + 1); return this.lastNumber(); } private Account$() { Account$.MODULE$ = this; this.lastNumber = 0; } }
①:伴生类首先生成了一个和伴生对象中的同名方法public static int newUniqueNumber() 。
②:伴生对象底层生成了一个静态的 Accoun$
类型的 MODULE$
静态对象。
③:伴生类的newUniqueNumber() 方法 返回一个通过 MODULE$
静态对象调用伴生对象的同名newUniqueNumber()方法。
同过上述过程了,实现了静态方法的调用。其中最最重要的是,伴生对象实现静态特性是依赖于
public static Account$ MODULE$ 这个静态对象实现的。伴生类通过MODULE$调用伴生对象的方法。
3.apply方法
我们通常会定义和使用对象的apply方法。可以通过:类名(参数1,…,参数N)方式创建对象。
- 类名(参数1,…,参数N) 这种形式会调用伴生对象的apply方法。返回的是一个伴生类的对象。例如
Array(1,2,3) //调用了Array的apply方法。 再来看看Array的源码 object Array extends FallbackArrayBuilding { def apply[T: ClassTag](xs: T*): Array[T] = {...} ...//内部定义了很多的apply方法 def apply(x: Unit, xs: Unit*): Array[Unit] = {...} }
- 定义apply方法的实例:
这样就可以这样来构建账号了:class Account private(val id: Int, initialbalance: Double){ private var balance = initialBalance ... } object Account{ def apply(initalBalance: Double) = { new Account(newUniqueNumber(),initialBalance) } ... }
val acct = Acount(100)
4.应用程序的对象
没有同名的伴生类的单例对象又被 称为孤立对象(standalone object)。孤立对象有很多种用途,包括将工具方法归集在一起,或定义 Scala 应用程序的入口。例如:
object Hello{
def main(args: Array[String]){
println("Hello,Scala")
}
}