Scala2.11.7学习笔记(七)类和对象

鲁春利的工作笔记,好记性不如烂笔头


1、apply

需要构造有参数需求的伴生对象时,可定义并使用apply方法。

在使用Array时,可以通过

val array01 = new Array[数据类型](数组元素个数);

来定义,也可以通过

val array02 = Array(元素值列表);

来定义。

实际上在使用Array(元素值列表时),调用的就是Array类的伴生对象Array的aply方法。

如:

val array = Array(1, 2, 3, 4, 5);

// 实际调用的是object Array的:

/** Creates an array of `Int` objects */
// Subject to a compiler optimization in Cleanup, see above.
def apply(x: Int, xs: Int*): Array[Int] = {
  val array = new Array[Int](xs.length + 1)
  array(0) = x
  var i = 1
  for (x <- xs.iterator) { array(i) = x; i += 1 }
  array
}

// 而实际Array类的定义为:
final class Array[T](_length: Int) 
extends java.io.Serializable with java.lang.Cloneable {......}

apply方法提供了直接通过伴生对象操作类实例,而无需手动new类示例的实现。

package com.lucl.scala.sample.classandobject.apply

// 类
class SimpleApplyTest (name : String) {
  def sayHi () {
    println("Hi, " + name);
  }
}

// 类的伴生对象
object SimpleApplyTest {
  def apply (name : String) : SimpleApplyTest = {
   new SimpleApplyTest (name);
  }
}

// 调用执行
object SampleApplyOps {
  def main(args: Array[String]): Unit = {
    // 实际调用Array的伴生对象的apply方法
    // def apply(x: Int, xs: Int*): Array[Int]
    val array = Array(1, 2, 3, 4, 5);  
    
    // 调用自定义的SimpleApplyTest的伴生对象的apply方法
    val at = SimpleApplyTest("luchunli");
    at.sayHi();
  }
}

    在这里可以通过类的伴生对象像操作函数那样直接使用apply方法。

    实际上,这是一种“约定”,只要你的object中定义了一个apply方法,你是可以直接使用object的名字作为函数名实现对这个apply方法的调用!

package com.lucl.scala.sample.classandobject.apply

// 定义class,只能在当前package中使用
final class ApplyTest private {
  // 属性字段name
  var name = "apply-test";
  // 带参的构造器
  def this (name : String) {
    this;
    this.name = name;
  }
  // 方法定义
  def getChar (index : Int) : Char = {
    name.charAt(index);
  }
}

// 类的伴生对象
object ApplyTest {
 // 无参的apply方法,返回类的对象
 def apply () : ApplyTest = {
   new ApplyTest("luchunli");
 }
 // 带参的apply方法,返回类的对象
 def apply (name : String) : ApplyTest = {
   new ApplyTest(name);
 }
 // 带参的apply方法,返回字符值
 def apply (index : Int) : Char = {
   val at = new ApplyTest();
   at.getChar(index);
 }
 
}

类定义在子包中,且类的定义修饰符为final和private,是无法在该包之外通过new的方式创建实例的。

package com.lucl.scala.sample.classandobject

// 通过_引入包的所有对象,类似于Java中的*
import com.lucl.scala.sample.classandobject.apply._;

//
object ApplyMain {
  def main(args: Array[String]): Unit = {
    // 调用伴生对象无参的apply方法
    val at1 = ApplyTest();
    println(at1.getChar(1));
    // 直接调用
    val at2 = ApplyTest("hello");
    println(at2.getChar(1));
    // 像函数式的调用
    val char = ApplyTest(1);
    println(char);
  }
}


2、case class

简单的来说,Scala的case class就是在普通的类定义前加case这个关键字,然后你可以对这些类来模式匹配。

通过Java的反编译命令查看普通的class与case class在scalac编译后的变化。

a、创建两个Scala代码文件:

CasePerson.scala

case class CasePerson(name : String)

NormalPerson.scala

class NormalPerson(name : String)

b、通过scalac进行编译

E:\home\source>scalac CasePerson.scala

E:\home\source>scalac NormalPerson.scala

E:\home\source>ls -l|grep Person
-rw-rw-rw-   1 user     group        1424 Jan  6 16:23 CasePerson$.class
-rw-rw-rw-   1 user     group        3812 Jan  6 16:23 CasePerson.class
-rw-rw-rw-   1 user     group          36 Jan  6 16:22 CasePerson.scala
-rw-rw-rw-   1 user     group         611 Jan  6 16:23 NormalPerson.class
-rw-rw-rw-   1 user     group          33 Jan  6 16:22 NormalPerson.scala

c、对比生成的class文件

普通class:

wKioL1aM0LTDWLLxAADoUmENJ1w535.jpg

case class:

wKioL1aM0KDD9UPuAAaMysgkAgc165.jpg

使用这个关键字,Scala编译器会自动为你定义的类生成一些成员。

scala> case class CasePerson(name : String);
defined class CasePerson

首先,编译器为case class生成一个同名的对象构造器(CasePerson$.class,即类的伴生对象和apply方法),可以直接通过CasePerson("luchunli")来创建类的实例,而无需使用new关键字。

scala> var person = CasePerson("luchunli");
person: CasePerson = CasePerson(luchunli)

其次,Scala编译器为case class的构造函数的参数创建以参数名为名称的属性,比如对于name可以通过.name直接访问。

scala> person.name;
res0: String = luchunli

第三,编译器为case class 构造了更自然的toString,hashCode和equals实现。

最后一点,Scala编译器为case class添加了一个Copy方法,这个copy方法可以用来构造类对象的一个可以修改的拷贝。

scala> val p2 = person.copy("zhangsan");
p2: CasePerson = CasePerson(zhangsan)

scala> p2.name
res2: String = zhangsan


3、模式匹配

case class带来的最大的好处是支持模式识别(Pattern matching)。

package com.lucl.scala.sample.classandobject

// 定义类
abstract class Person;
case class Student(name : String, grade : Int) extends Person;
case class Worker(name : String, salary : Double) extends Person;
case object Shared extends Person;

// 测试
object CaseClassOps {
  def main(args: Array[String]): Unit = {
    def caseOps (person : Person) {  // 内部函数(或称为本地函数)
      person match {
        case Student(name, grade) => println(name + " : " + grade);
        case Worker(name, salary) => println(name + " : " + salary);
        case Shared => println("not match");
      } 
    }
    
    // 
    caseOps(Student("zhangsan", 80));
    caseOps(Worker("lisi", 5000.0));
    caseOps(Shared);
    
    // 
    val worker = Worker("wangwu", 8000.0);
    val worker2 = worker.copy(salary=9000.0);
    println(worker2.name + "|" + worker2.salary);
  }
  
}


你可能感兴趣的:(object,apply,Class)