• Programming In Scala笔记-第四章、类和对象


      类似于Java,Scala中也有类和对象的概念。

    一、类、属性和方法

    1、类
      类是对一类事物的抽象,当一个类被定义后,就可以以该定义为模板,定义该类的一系列对象。比如说有以下一个模板

    人类:
      有姓名;
      有一个大脑;
      有四肢;
      有性别;
      会思考;
      有语言能力;

      上面这个模板可以认为是对正常人类的一个最简单的抽象,那么有两个人张三和李四,就是人类这一模板的具体实例了。

      在Scala中,用class关键字定义一个类,用new关键字实例化该类的一个对象。一个简单的类定义和实例化如下所示

    class ChecksumAccumulator {
      // 具体定义代码省略
    }
    
    new ChecksumAccumulator

      在上面代码中省略的部分可以写入一些该类的属性和方法。所谓属性是指该类的一些特性,比如上面例子中的姓名,性别等,都可以认为是人类的一个属性。而方法是指该类可以执行的一些逻辑片段,比如上面例子中定义人类的走路方式为站立行走,那么正常的两个人类张三和李四的行走方式都是站立行走了。
      有关类的属性和方法,接下来再进行分析。

    2、属性
      属性可以用var和val来定义,比如
      

    class ChecksumAccumulator {
      var sum = 0
    }

      那么,使用下面代码new出两个对象  

    val acc = new ChecksumAccumulator
    val csa = new ChecksumAccumulator

      acc和csa都有一个属性sum。并且值都是0。如下图所示,前面两个圆圈表示acc和csa对象的具体存储,指向同一个0是由于0在常量池中只有一个。
      这里写图片描述
      执行acc.sum=3可以将acc的sum属性更新为3,那么现在的情况是这样的
      这里写图片描述
      
      从这里可以看出,虽然acc是以val来定义的,但是可以修改其中的属性值。定义为val的作用是,如果将acc指向另一个ChecksumAccumulator对象时就会报错了。一般来说,最好将属性定义为private类型,private类型的属性,只能由该类中的方法进行访问和修改,尽量避免外部代码直接修改对象的属性值。如果需要属性发生变化,可以通过方法来实现。

    3、方法
      方法以def开头,里面包含了一段可执行的代码片段。这里需要注意的是,方法的参数是val类型的,如果在代码中更改参数值,是会报错的。 

    class ChecksumAccumulator {
      private var sum = 0
      def add(b: Byte): Unit = {
        sum += b
      }
      def checksum(): Int = {
        return ~(sum & 0xFF) + 1
      }
    }

      上面代码中定义了两个方法,根据这两个方法的定义,可以进行一定的简化,

    class ChecksumAccumulator {
      private var sum = 0
      def add(b: Byte): Unit = sum += b
      def checksum(): Int = ~(sum & 0xFF) + 1
    }

      上一篇博客中提到过,返回值为Unit的语句,会产生side effects。这里add方法的side effects是改变了sum变量的值。side effects产生的影响,要么是改变了某个mutable变量的值,要么是产生了一些I/O,这些并不会影响其他代码的执行。
      对于这种返回值为Unit的方法,可以去掉前面的Unit =部分,将函数体用花括号包起来。

    class ChecksumAccumulator {
      private var sum = 0
      def add(b: Byte) { sum += b }
      def checksum(): Int = ~(sum & 0xFF) + 1
    }

      上面代码中add方法的写法,即使其执行逻辑的返回值不为Unit,这种写法也显示的将该方法返回值设置为Unit了。

    二、语句划分

      不知道有没有发现在上面的代码中并不像Java代码那样,每一句代码的结尾用分号进行分割。在Scala中每一行语句的结尾并不需要写一个分号,一般情况下,以换行作为默认的分隔符。只有当一行中写多句代码时,代码之间才需要以分号进行分隔。

    val s = "hello"; println(s)

      Scala默认换行为代码直接的分隔符,

    x
    + y

    会被认为是两行代码,x和+ y。但是如果确实同一句代码需要换行应该怎么办?可以用圆括号将同一句代码包围起来,在圆括号中可以使用换行。

    (x
    + y)

    其实就是x + y的换行形式。

    三、单例对象

      虽然Scala是函数式编程语言,但是说到面向对象,其实Scala比Java还严格。在Java中,一个类还可以有一些静态属性或者静态方法,这样在不new一个对象时,也可以使用其中很多的静态属性和方法。但是Scala没有静态属性和方法一说,任何属性和方法的使用,都是通过对象来进行的。
      如果确实需要在Scala中直接使用类名调用属性和方法应该怎么做?Scala通过提供一种单例对象,或者说伴生对象巧妙的实现了这一功能。前面我们定义了一个ChecksumAccumulator类,我们用另一个关键字object,同样使用ChecksumAccumularot这个名字,就生成了一个ChecksumAccumulator类的伴生对象了。

    import scala.collection.mutable.Map
    object ChecksumAccumulator {
      private val cache = Map[String, Int]()
      def calculate(s: String): Int =
        if (cache.contains(s))
          cache(s)
        else {
          val acc = new ChecksumAccumulator
        for (c <- s)
          acc.add(c.toByte)
        val cs = acc.checksum()
        cache += (s -> cs)
        cs
      }
    }

    接下来我们就可以使用ChecksumAccumulator.calculate直接进行计算了。从字面上看,和Java中调用静态方法是一样的。
      上面的ChecksumAccumulator对象称为ChecksumAccumulator类的伴生对象,ChecksumAccumulator类称为ChecksumAccumulator对象的伴生类。伴生对象和伴生类,需要写在同一个源文件中,并且伴生类和伴生对象之间可以互相访问对方的private成员。

    四、代码运行的入口

      在Java中,一个Application是以某个Class中的public static void main(String[] args)的方法开始的。想要运行一段Scala代码,需要定义一个单例对象,并且在该单例对象中写一个main方法,这个方法接收的参数是一个String类型数组,返回值类型为Unit。

    import ChecksumAccumulator.calculate
    object Summer {
      def main(args: Array[String]) {
        for (arg <- args)
          println(arg +": "+ calculate(arg))
      }
    }

      一个object中的main方法,是一个Application的入口。类似于java中的main方法。
      
      不过Scala也提供了一个名为Application的trait,可以简单的把trait理解成Java中的接口。如果object继承自该trait的话,那么不需要main方法也能运行起来。把原来需要写在main方法中的代码,直接写在随后的花括号中。

    import ChecksumAccumulator.calculate
    object FallWinterSpringSummer extends Application {
      for (season <- List("fall", "winter", "spring"))
        println(season + " : " + calculate(season))
    }

    这段代码之所以能够运行,从Application中可以看到,这里面已经实现了一个main方法。上面写的这些代码,在这个类被初始化时由主构造方法执行。

  • 相关阅读:
    老狼老狼几点了
    佐藤 Sato CL4NX 通过发送指令打印RFID标签
    HonyWell Intermec 通过Fingerprint指令打印RFID标签
    微信小程序中用echarts地图并点亮城市geo和effectScatter
    .NET Core
    数组去重
    Oracle
    CSS积累
    WebADNuke整理
    .net 基础面试题
  • 原文地址:https://www.cnblogs.com/wuyida/p/6300226.html
Copyright © 2020-2023  润新知