鲁春利的工作笔记,好记性不如烂笔头
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:
case class:
使用这个关键字,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); } }