2.7 将常用代码放在包对象中
1. 问题描述
你想要使方法、字段和其他代码处于包级别,而不需要class或者object
2. 解决方案
将代码放置在包对象下面,如将你的代码放置在package.scala文件中,例如,如果你想要代码被com.hust.grid.leesf.model包下所有类可用,那么创建一个位于com/hust/grid/leesf/model目录下的package.scala文件,在package.scala中,在包声明中移除model,并且以其作为名字来创建包,大致如下
package com.hust.grid.leesf package object model {
其他代码放置在model中,如下所示
package com.hust.grid.leesf package object model { // field val MAGIC_NUM = 42 // method def echo(a: Any) { println(a) } // enumeration object Margin extends Enumeration { type Margin = Value val TOP, BOTTOM, LEFT, RIGHT = Value } // type definition type MutableMap[K, V] = scala.collection.mutable.Map[K, V] val MutableMap = scala.collection.mutable.Map }
此时,在com.hust.grid.leesf.model包下面类、对象、接口等可以随意访问上述定义的字段、方法等
package com.hust.grid.leesf.model object MainDriver extends App { // access our method, constant, and enumeration echo("Hello, world") echo(MAGIC_NUM) echo(Margin.LEFT) // use our MutableMap type (scala.collection.mutable.Map) val mm = MutableMap("name" -> "Al") mm += ("password" -> "123") for ((k,v) <- mm) printf("key: %s, value: %s\n", k, v) }
3. 讨论
最疑惑的是将package对象放在哪里,其包名和对象名
如果你想要让你的代码在com.hust.grid.leesf.model包中可见,那么将package.scala放在com/hust/grid/leesf/model目录下,而在package.scala中,其包名应该如下
package com.hust.grid.leesf
然后使用model作为对象名
package object model {
最后大致如下
package com.hust.grid.leesf package object model {
包中可以存放枚举类型、常量和隐式转换
2.8 不使用new关键字来创建对象实例
1. 问题描述
当不使用new关键字来创建对象时,Scala代码会显得相对简洁,如下所示
val a = Array(Person("John"), Person("Paul"))
2. 解决方案
有两种方式
· 为类创建伴生对象,然后定义apply方法,其签名与构造方法签名相同
· 将类定义为case类
为Person对象定义了伴生对象,然后定义apply方法并接受参数
class Person { var name: String = _ } object Person { def apply(name: String): Person = { var p = new Person p.name = name p } }
现在你可以不使用new关键字来创建Person对象了
val dawn = Person("Dawn") val a = Array(Person("Dan"), Person("Elijah"))
Scala编译器会对伴生对象中的apply进行特殊处理,让你不使用new关键字即可创建对象
将类定义为case类,并且接受相应参数
case class Person (var name: String)
现在可以采用如下方法创建对象
val p = Person("Fred Flinstone")
Scala会为case类的伴生对象创建apply方法
3. 讨论
编译器会对伴生对象的apply做特殊处理,这是Scala的语法糖
val p = Person("Fred Flinstone")
上述代码会被转化为如下代码
val p = Person.apply("Fred Flinstone")
apply方法是工厂方法,Scala的此语法糖让你不用new关键字即可创建对象
可以在伴生对象中创建多个apply方法,这样相当于多个构造函数
class Person { var name = "" var age = 0 } object Person { // a one-arg constructor def apply(name: String): Person = { var p = new Person p.name = name p } // a two-arg constructor def apply(name: String, age: Int): Person = { var p = new Person p.name = name p.age = age p } }
可以使用如下方法创建对象
val fred = Person("Fred") val john = Person("John", 42)
为了给case类创建多个构造函数,需要知道case类背后的逻辑
当使用scala编译器编译case类时,你会发生其生成了两个文件,Person$.class和Person.class文件,当使用javap反编译Person$.class文件时,其输出如下
其包含了一个返回Person对象的apply方法
public Person apply(java.lang.String);
String对应的是case类中的name
case class Person (var name: String)
使用javap命令可以看到在Person.class中为name生成的getter和setter函数
在如下代码中,存在case类和apply方法
// want accessor and mutator methods for the name and age fields case class Person (var name: String, var age: Int) // define two auxiliary constructors object Person { def apply() = new Person("<no name>", 0) def apply(name: String) = new Person(name, 0) }
由于name和age都是var的,所以会生成getter和setter,在object中定义了两个apply函数,因此可以使用如下三种方式来生成Person对象
object Test extends App { val a = Person() val b = Person("Al") val c = Person("William Shatner", 82) println(a) println(b) println(c) // test the mutator methods a.name = "Leonard Nimoy" a.age = 82 println(a) }
其结果如下
Person(<no name>,0) Person(Al,0) Person(William Shatner,82) Person(Leonard Nimoy,82)
2.9 使用apply来实现工厂方法
1. 问题描述
为了让子类声明应该创建哪种类型的对象,并且只在一处能够创建对象,你想要实现工厂方法
2. 解决方案
可以使用伴生对象的apply方法来实现工厂方法,你可将工厂实现算法放置在apply方法中
假设你想要创建一个Animal工厂,并且返回Cat和Dog,在Animal类的伴生对象中实现apply方法,你就可以使用如下方式创建不同对象
val cat = Animal("cat") // creates a Cat val dog = Animal("dog") // creates a Dog
首先需要创建一个Animal的trait
trait Animal { def speak }
然后在相同文件中创建伴生对象,创建实现Animal的类,一个合适的apply方法
object Animal { private class Dog extends Animal { override def speak { println("woof") } } private class Cat extends Animal { override def speak { println("meow") } } // the factory method def apply(s: String): Animal = { if (s == "dog") new Dog else new Cat } }
然后就可以使用如下语句创建不同对象
val cat = Animal("cat") // creates a Cat val dog = Animal("dog") // creates a Dog
3. 讨论
如果不使用apply方法来实现工厂方法,也可以使用如下的getAnimal方法来实现上述功能
// an alternative factory method (use one or the other) def getAnimal(s: String): Animal = { if (s == "dog") return new Dog else return new Cat }
然后可以使用如下方法创建不同对象
val cat = Animal.getAnimal("cat") // returns a Cat val dog = Animal.getAnimal("dog") // returns a Dog
以上两种方法都是可行的
三、总结
本篇学习了Scala中的object及其相应的用法,其在Scala的实际编程中应用也是非常广泛,也谢谢各位园友观看~