还是老样子,对java不是很熟悉,所以有些概念不是很理解。还是应用为主,初步了解用法,后面有机会再Update。
单例对象
在idea中,经常会新建一个object,在里面定义N多个函数,最后用main函数执行。那这个object是干嘛的?先定义个object瞅瞅:
object objDemo{
var name = "jack"
def printName(){
println("My Name is "+this.name)
}
def printAge(age:Int){
printf("I am %s years old",age)
}
}
defined object objDemo
objDemo.name= "leo"
objDemo.name: String = leo
objDemo.name
res13: String = leo
objDemo.printName
My Name is leo
objDemo.printAge(18)
I am 18 years old
从上面的例子中可以看出:
- object提供了类似class的功能
- 比如,可能提供属性和方法,也可以修改var的属性
- 方法可以自带参数,调用方法时直接传参进去
- 但是调用时,无需通过new Class的方式,实例化对象
**对象本质上可以提供类的所有特性,但是不能提供构造器参数。**对象一般用在:
- 作为存放工具函数和常量的地方
- 高效的共享单个不可变实例
- 需要用单个实例来协调某个服务时
对于上面的三点,我只了解第一点。项目开发时,会定义个Constant对象,把常量放进去,比如库表名称,hdfs路径等等。另外常用函数也会单独封装在对象中,如在util子包下新建对象,封装读取数据库的方法、操作hdfs等方法。
伴生类和伴生对象
class ScalaPerson {
var name : String = _
}
object ScalaPerson {
var sex : Boolean = true
}
如上所示:class ScalaPerson是伴生类,object ScalaPerson 是伴生对象。
- Scala中伴生类和伴生对象的名称一样,如上均为ScalaPerson,前面的声明关键词分别为class和object
- 他们必须在一个脚本文件中
- Scala中伴生对象采用object关键字声明,伴生对象中声明的全是 "静态"内容,可以通过伴生对象名称直接调用
- 伴生对象对应的类称之为伴生类,伴生对象的名称应该和伴生类名一致
- 编译之后,
还是看个例子:class ScalaPerson生成ScalaPerson.class文件,object ScalaPerson生成ScalaPerson$.class文件
class ScalaPerson() {
val name = "James"
def ff(){
println("hahah")
}
}
object ScalaPerson {
var sex : Boolean = true
def printName(){
println("My Name is Kobe")
}
}
defined class ScalaPerson
defined object ScalaPerson
ScalaPerson.sex
res20: Boolean = true
ScalaPerson.printName()
My Name is Kobe
可以看出伴生对象中的属性和方法都可以通过伴生对象名(类名)直接调用访问
下面是一些底层细节,我不是很明白,先贴在这:
- 从语法角度来讲,所谓的伴生对象其实就是类的静态方法和成员的集合
- 从技术角度来讲,scala还是没有生成静态的内容,只不过是将伴生对象生成了一个新的类,实现属性和方法的调用
- 从底层原理看,伴生对象实现静态特性是依赖于 public static final MODULE$ 实现的
- 如果 class A 独立存在,那么A就是一个类, 如果 object A 独立存在,那么A就是一个"静态"性质的对象[即类对象], 在 object A中声明的属性和方法可以通过 A.属性 和 A.方法 来实现调用
截止目前,我们似乎只是知道伴生类和伴生对象的一些概念,而且伴生类和伴生对象之间并没有发生什么联系,如果两个"类"不能发生点啥,同名有什么意义呢?直接单例对象就可以了。那么伴生类和伴生对象之间怎么交互呢?
类和它的伴生对象可以互相访问私有属性
class ScalaPerson() {
private val name = "James"
// val sex = ScalaPerson.sex
// val age = ScalaPerson.Test
def ff(){
printf("%s is a %s.\n",name,ScalaPerson.sex)
ScalaPerson.Test
}
}
object ScalaPerson {
private var sex = "boy"
def printName(boy:ScalaPerson){
printf("My Name is %s",boy.name)
}
private def Test{
println("This is private method in object ScalaPerson")
}
}
defined class ScalaPerson
defined object ScalaPerson
val boy = new ScalaPerson()
boy.ff()
print("\n")
ScalaPerson.printName(boy)
James is a boy.
This is private method in object ScalaPerson
My Name is James
boy: ScalaPerson = ScalaPerson@6efb568f
- 伴生类ScalaPerson中私有属性name,可以在伴生对象ScalaPerson中使用(printName调用了boy的私有属性 ),反之sex属性亦可在伴生类中使用
- 伴生对象ScalaPerson中的私有方法也可以在伴生类ScalaPerson中使用
再看个实际案例:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?
object ChildJoinGame {
def main(args: Array[String]): Unit = {
Child.startGame()
//创建三个小孩
val child0 = new Child("James")
val child1 = new Child("Wade")
val child2 = new Child("Paul")
val child3 = new Child("Anthony")
Child.joinGame(child0)
Child.joinGame(child1)
Child.joinGame(child2)
Child.joinGame(child3)
Child.showNum()
}
}
// 伴生类,主构造器参数为cName
class Child(cName: String) {
var name = cName
}
object Child {
//统计共有多少小孩的属性
var totalChildNum = 0
// 这一步相当于计数器清零
def startGame(){
println("------ Game begins! ------")
totalChildNum = 0
}
def joinGame(child: Child): Unit = {
printf("%s is coming\n", child.name)
//totalChildNum 加1
totalChildNum += 1
}
def showNum(): Unit = {
printf("There are %d children\n", totalChildNum)
}
}
ChildJoinGame.main(Array[String]())
------ Game begins! ------
James is coming
Wade is coming
Paul is coming
Anthony is coming
There are 4 children
defined object ChildJoinGame
defined class Child
defined object Child
可以看到已经解决了上述的需求,代码逻辑比较简单:
- 伴生对象提供了计数功能
- 游戏开始前,通过伴生对象的startGame,清零计数器
- 游戏开始每新建一个对象(加入一个小朋友),调用伴生对象中计数函数joinGame
伴生对象中apply方法
在伴生对象中定义apply方法,可以实现:类名(参数)
方式来创建对象实例,还是看代码:
//案例演示apply方法.
class Pig(pName:String) {
var name: String = pName
}
object Pig {
//编写一个apply
def apply(pName: String): Pig = new Pig(pName)
def apply(): Pig = new Pig("What's your name")
}
defined class Pig
defined object Pig
var pig1 = new Pig("ZhuBajie1")
var pig2 = Pig("ZhuBajie2")
var pig3 = Pig()
printf(" pig1 = %s \n pig2 = %s \n pig3 = %s \n",pig1.name,pig2.name,pig3.name)
pig1 = ZhuBajie1
pig2 = ZhuBajie2
pig3 = What's your name
pig1: Pig = Pig@7902e3df
pig2: Pig = Pig@c612596
pig3: Pig = Pig@1e48dc16
就上面这个例子而言,似乎只是用伴生对象新建了个伴生类,少了个new而已,有啥实际意义吗?《快学Scala》中说省去new关键字之后,在使用嵌套表达式时比较方便,如Array(Array(1,2,3),Array(3,2,1))
2020-02-27 于南京市栖霞区