Scala语言基础(四)面向对象(二)单例对象、伴生对象

1. 单例对象

Scala没有静态方法或静态字段,你可以用Object这个语法结构来达到同样的目的。对象定义了某个类的单个实例,包含了你想要的特性。例如:

object ObjectTest {
  def main(args: Array[String]): Unit = {

    //如果属性和方法定义在object组件中,就可以直接使用类型名称访问
    //和java中的static语法一致
    println(ObjectTest11.id)
    println(ObjectTest11.m1(3))

    //也可以构建对象
    val obj_1 = ObjectTest11
    val obj_2 = ObjectTest11
    println(obj_1 == obj_2)//true
    println(obj_1 eq obj_2)//true
    println(obj_1 ne obj_2)//false
    println(obj_1.equals(obj_2))//true

    val obj22:ObjectTest22= new ObjectTest22()
    println(obj22.id)
    println(obj22.m1(3))

    val obj33_1 = new ObjectTest33("xh",18)
    val obj33_2 = new ObjectTest33("xh", age = 18)
    println(obj33_1 == obj33_2)//true 默认是调用equals方法
    println(obj33_1 eq obj33_2)//false 比较引用地址
    println(obj33_1 ne obj33_2)//true 比较引用地址
    println(obj33_1.equals( obj33_2))//true 比较toString方法的值 必须要重写类的toString方法和equals方法,否则还是比较对象的引用地址
  }
}

// object修饰的是单例对象
object ObjectTest11 {
  val id:Int = 11
  def m1(x:Int) = x+1
}

class ObjectTest22 {
  val id:Int = 11
  def m1(x:Int) = x+1
}

class ObjectTest33(name:String, age:Int) {
  def m1(x:Int) = x+1

  override def toString: String = name +"-" + age

  override def equals(obj: Any): Boolean = this.toString.equals(obj.toString)
}

对象本质上可以拥有类的所有特性—它甚至可以扩展其他类或特质,只有一个例外:不能提供构造器参数。

2. 伴生对象

在Java或C++中,通常会用到即有实例方法又有静态发给发的类。在Scala中,你可以通过类和与类同名的“伴生”对象来达到同样的目的。例如:

class Account {
    val id = Account.newUniqueNumver()
    private var balance = 0.0

    def deposit(amount: Double) = {
        balance += amount
        balance
    }
}

object Account { // 伴生对象
    private var lastNumber = 0

    private def newUniqueNumver() = {
        lastNumber += 1
        lastNumber
    }
}

类和它的伴生对象可以相互访问私有特性。它们必须存在于同一个源文件中。

3. apply类

我们通常会定义和使用对象的apply方法。当遇到如下形式的表达式时,apply方法就会被调用:
Object(参数1, …, 参数N)
通常,这样一个apply方法返回的是伴生类的对象。
例如,Array对象定义了apply方法,让我们可以用下面这样的表达式来创建数组:

Array(“Mary”, “had”, “a”, “little”, “lamb”)
为什么不用构造器呢?对于嵌套表达式而言,省去new关键字会方便很多,例如:
Array(Array(1, 7), Array(2, 9))

注意:

Array(100)和new Array(100)很容易搞混。前一个表达式调用的是apply(100), 输出一个单元素(整数100)的Array[Int]; 而第二个表达式调用的是构造器this(100),结果是Array[Nothing], 包含了100个null元素。

apply方法的示例:

class Account private (val id: Int, initialBalance: Double) {
private var balance = initialBalance

}

object Account { // 伴生对象
def apply(initialBalance: Double) =
new Account(newUniqueNumber(), initialBalance)

}

这样一来你就可以用如下代码来构造账号了:

val acct = Account(1000.0)

4. 应用程序对象

每个Scala程序都必须从一个对象的main方法开始,这个方法的类型为Array[String]=>Unit:

object Hello{
    def main(args: Array[String]): Unit = {
        println("Hello, World!")
    }
}

除了每次都提供自己的main方法外,你亦可以扩展App特质,然后将程序代码放入构造器方法体内:

object Hello extends App {
    println("Hello, World!")
}

如果需要命令行参数,则可以通过args属性得到:

object Hello extends App {
    if (args.length > 0)
        println("Hello, " + args(0))
    else
        println("Hello, World!")
}

如果调用该程序时设置了scala.time选项的话,程序退出时会显示逝去的时间。

$ scalac Hello.scala
$ scala -Dscala.time Hello Fred
Hello, Fred
[total 4ms]

所有这些,都是来源于App特质扩展另一个特质DelayedInit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedInit方法中。App特质的main方法捕获到命令行参数,调用delayedInit方法,并且还可以根据要求打印出逝去的时间。

5. 枚举

和Java或C++不同,Scala并没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举类。
每次条用Value 对象都返回内部类的新实例,该内部类也叫做Value.
或者也可以向Value出入Id、名称,或者两个参数都传,下面是不传参数:

object TrafficLightColor extends Enumeration{
    val Red, Yellow, Green = Value
    // 是如下代码的简写
    //val Red = Value
    //val Yellow = Value
    //val Green = Value
    def main(args: Array[String]): Unit = {
        println(Red) // 还是字段名本身
        println(Red.id) // 0 id从0开始,后面依次加1
    }
}

传参数:

object TrafficLightColor extends Enumeration {
    // 是如下代码的简写
    val Red = Value(0, "stop")
    val Yellow = Value(10) // id为10,名称为Yellow
    val Green = Value("go") // id为11(在前一个基础上增1)名称为go
    def main(args: Array[String]): Unit = {
        println(Red) // stop
        println(Red.id) // 0 id从0开始
        println(Green) // go
        println(Green.id) // 11
    }
}
object TrafficLightColor extends Enumeration {
    
    val Red = Value(0, "stop")
    val Yellow = Value(10) // id为10,名称为Yellow
    val Green = Value("go") // id为11(在前一个基础上增1)名称为go
    
    def main(args: Array[String]): Unit = {
        println(TrafficLightColor.Red) // 其他对象调用
        
        for (c <- TrafficLightColor.values) println(c.id + ": " + c) // 输出所有枚举id和值
    }
}

6. 样例类

会默认将equals和toString方法实现,不用我们亲自实现

object ObjectTest {
  def main(args: Array[String]): Unit = {
    //普通类
    val obj_1 = new ObjectTest11("xh",18)
    val obj_2 = new ObjectTest11("xh", 18)
    println(obj_1 == obj_2)//false
    println(obj_1 eq obj_2)//false
    println(obj_1 ne obj_2)//true
    println(obj_1.equals(obj_2))//false
    println("---------------------------")

    //样例类
    val obj_11 = new ObjectTest22("xh",18)
    val obj_22 = new ObjectTest22("xh", 18)
    println(obj_11 == obj_22)//true
    println(obj_11 eq obj_22)//false
    println(obj_11 ne obj_22)//true
    println(obj_11.equals(obj_22))//true

  }
}

// object修饰的是单例对象
class ObjectTest11(name:String, age:Int) {
  def m1(x:Int) = x+1
}
case class ObjectTest22(name:String, age:Int) {
  def m1(x:Int) = x+1
}

总结:

  1. 自定义类,如果不是样例类,需要重写toString方法和equals方法,这样下面的比较就会和样例类实现相同的结果
    a. ==和equals方法比较toString方法所得的结果
    b. eq(是否相等)比较对象的引用地址
    c. ne(不相等吗)比较对象的引用地址
  2. 如果自定义类不是样例类,也没重写toString和equals方法,这样上面的比较方法都是比较对象的引用地址了
  3. 单例对象类似Java中的类的静态方法,直接使用类名称调用属性方法,又因是单例对象,所以四个比较方法除ne外都是相等的
    样例类
    1,构造器中的参数如果不被声明为var的话,它默认是val类型的,一般推荐构造器中的参数声明为var
    2,自动创建伴生对象,同时在里面给我们实现apply方法,使使用的时候可以不使用new关键字创建对象
    3,伴生对象中同样会帮助我们是想unapply方法,从而使case class应用于模式匹配
    4,实现自家的toString、hashCode、copy、equals方法
    除此之外,case class 与scala中其他普通类没区别
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值