ScalaNote10-单例对象、伴生对象、伴生类

  还是老样子,对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,前面的声明关键词分别为classobject
  • 他们必须在一个脚本文件中
  • 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 于南京市栖霞区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值