美文网首页
第十讲 对象

第十讲 对象

作者: 农民工进城 | 来源:发表于2018-04-05 11:05 被阅读0次

    在本篇中,你将会学到何时使用Scala的object语法结构。在你需要某个类的单个实例时,或者想为其他值或函数找一个可以挂靠的地方时,你就会用到它。

    本章要点:

    • 用对象作为单例或存放工具方法
    • 类可以拥有—个同名的伴生对象
    • 封装、继承和多态
    • 对象的apply方法通常用来构造伴生类的新实例
    • 如果不想显式定义main方法,可以用扩展App特质的对象
    • 你可以通过扩展Enumeration对象来实现枚举

    10.1 scala工具类(object)

    Scala没有像java一样的静态方法或静态字段,但是可以通过object这个语法结构来达到同样目的。对象定义了某个类的单个实例或工具类。例如:

    object Accounts {
      private var lastNumber = 0
      def newUniqueNumber() = {
        lastNumber += 1;
        lastNumber
      }
      def main(args: Array[String]): Unit = {
        Accounts.newUniqueNumber()
      }
    }
    

    当你在应用程序中需要一个新的唯一账号时,调用Accounts.newUniqueNumber()即可。对象的构造器在该对象第一次被使用时调用。在本例中,Accounts的构造器在Accounts.newUniqueNumber()的首次调用时执行。如果一个对象从未被使用,那么其构造器也不会被执行。

    对象本质上可以拥有类的所有特性,它甚至可以扩展其他类或特质。只有一个例外:你不能提供构造器参数。对于任何你在Java或C++中会使用单例对象的地方,在Scala中都可以用对象来实现:

    • 作为存放工具函数或常量的地方
    • 高效地共享单个不可变实例
    • 需要用单个实例来协调某个服务时,可参考单例模式

    10.2 伴生对象

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

    class Account {
      val id = Account.newUniqueNumber()
      private var balance:Double = 0
      def deposit(amount: Double) {
        balance += amount
      }
    }
    
    object Account { // 伴生对象
      private var lastNumber = 0
      private def newUniqueNumber() = {
        lastNumber += 1;
        lastNumber
      }
      def main(args: Array[String]): Unit = {
        var account=new Account
        account.deposit(10)//调用实例方法
        Account.newUniqueNumber();//调用静态方法
      }
    }
    
    

    10.3 封装、继承和多态

    第一讲曾说过scala具有面向对象编程的特征,那么它当然就具有和java类似的抽象和继承,多态的特点。
    例子如下:

    abstract class UndoableAction(val description: String) {
      def undo(): Unit
      def redo(): Unit
    }
    class DoNothingAction extends UndoableAction("Do nothing") {
      override def undo() {
        print("****undo******")
      }
      override def redo() {
        print("*****redo*****")
      }
    }
    object UndoableAction{
        def main(args: Array[String]): Unit = {
        var action:UndoableAction=new DoNothingAction
        action.redo()
      }
    }
    

    10.4 apply方法

    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, val initialBalance: Double) {
      private var balance = initialBalance
    }
    
    object Account {//伴生对象
      private var lastNumber = 0
      private def newUniqueNumber() = {
        lastNumber += 1;
        lastNumber
      }
      def apply(initialBalance: Double) =
    
        new Account(newUniqueNumber(), initialBalance)
    
      def main(args: Array[String]): Unit = {
        var account = Account.apply(2222);
        println("balance:" + account.balance + "    id:" + account.id)
        var account1 = Account(3333)//此时,不用new命令了,效果一样,默认调用的apply方法
        println("balance:" + account1.balance + "    id:" + account1.id)
      }
    }
    
    

    10.5 应用对象

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

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

    扩展App特质

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

    object Hello extends App {
      println("Hello, World! ")//也可作为入口,直接执行
    }
    

    和java一样,如果你需要命令行参数,则可以通过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特质扩展自另一个特质Delayedlnit,编译器对该特质有特殊处理。所有带有该特质的类,其初始化方法都会被挪到delayedlnit方法中。App特质的main方法捕获到命令行参数,调用delayedlnit方法,并且还可以根据要求打印出逝去的时间。

    10.5 scala枚举

    Scala没有枚举类型。不过,标准类库提供了一个Enumeration助手类,可以用于产出枚举。
    定义一个扩展Enumeration类的对象并以Value方法调用初始化枚举中的所有可选值。例如:

    object TrafficLightColor extends Enumeration {
      val Red, Yellow, Green = Value
    }
    

    也可以写成这样:

    object TrafficLightColor extends Enumeration {
      val Red = Value
      val Yellow = Value
      val Green = Value
    }
    

    每次调用Value方法都返回内部类的新实例,该内部类也叫做Value。或者,你也可以向Value方法传人ID、名称,或两个参数都传:

        val Red = Value(0, "Stop")
        val Yellow = Value(10) // 名称为"Yellow"
        val Green = Value("Go") // ID为11
    

    如果不指定,则ID在将前一个枚举值基础上+1,从零开始。缺省名称为字段名。
    定义完成后,你就可以用TrafficLightColor.Red、TrafficLightColor.Yellow等来引用枚举值了。如果这些变得冗长烦琐,·则可以用如下语句直接引入枚举值:

    object TrafficLightColor extends Enumeration {
        val Red, Yellow, Green=Value;
    }
    object Test {
      def doWhat(color: TrafficLightColor.Value) = {
        if (color == TrafficLightColor.Red) "stop"
        else if (color == TrafficLightColor.Yellow) "hurry up"
        else "go"
      }
      
      def main(args: Array[String]): Unit = {
        print(Test.doWhat(TrafficLightColor.Red));
      }
    }
    

    枚举值的ID可通过id方法返回,名称通过toString方法返回。对TrafficLightColor.values的调用输出所有枚举值的集:

        for (c <- TrafficLightColor.values) {
          println(c.id + ":" + c)
        }
    

    最后,你可以通过枚举的ID或名称来进行查找定位,以下两段代码都输出TrafficLightColor.Red对象:

        print(TrafficLightColor(0))// 将调用Enumeration.apply
        print(TrafficLightColor.withName("Red"));
    

    相关文章

      网友评论

          本文标题:第十讲 对象

          本文链接:https://www.haomeiwen.com/subject/jherhftx.html