我在谷歌搜索并发现并深入了解Anonymous,Singleton& Scala中的伴随对象
我在scala中发现了
没有引用名称的对象称为匿名对象。如果您不想进一步重复使用它,最好创建一个匿名对象。
Singleton对象是一个使用object关键字而不是类声明的对象。调用在singleton对象中声明的方法并不需要任何对象,也没有静态概念。因此,scala创建了一个单例对象,为程序执行提供了入口点。
在scala中,当你有一个与singleton对象同名的类时,它被称为伴随类,而singleton对象被称为companion对象。伴随类及其伴随对象都必须在同一源文件中定义。
那么,如果我们不想重用,为什么匿名对象是好的呢? Singleton很容易理解,但是Companion Object的真正目的是什么?我的意思是编写伴侣类和伴侣对象的规则背后的故事都必须在同一个源文件中定义? Companion Object的唯一原因是我们有一个与singleton对象同名的类吗?
我想Scala中的这些功能应该有一些重要原因。有什么解释或是否有资源可以从中了解更多有关Scala对象的信息?
答案 0 :(得分:7)
这是一个很长的答案,但我希望它澄清了一些潜在的使用场景。
那么,如果我们不想重用,为什么匿名对象是好的?
我认为与其他两个术语不同的是" 匿名对象"在Scala世界中没有明确定义。我可以想到一些可能被称为的事情:
foldLeft
。您希望在那里传递初始值,但您通常不需要给它任何名称,因为这是一次性对象。另一种情况是这样的对象包含你想要使用的一些逻辑(一种strategy pattern)。请考虑使用标准ParIterableLike
def count(p: T => Boolean): Int = {
tasksupport.executeAndWaitResult(new Count(p, splitter))
}
此特定实现使用命名方法tasksupport
,因为它希望它可以自定义。但如果不是这样,它可能就像是
def count(p: T => Boolean): Int = {
new ExecutionContextTaskSupport.executeAndWaitResult(new Count(p, splitter))
}
和new ExecutionContextTaskSupport
将是一个匿名对象。
应该被称为" 匿名类型对象"的东西。
case class Resident(name: String, age: Int, role: Option[String])
import play.api.libs.json._
implicit val residentReads = Json.reads[Resident]
// In a request, a JsValue is likely to come from `request.body.asJson`
// or just `request.body` if using the `Action(parse.json)` body parser
val jsonString: JsValue = Json.parse(
"""{
"name" : "Fiver",
"age" : 4
}"""
)
val residentFromJson: JsResult[Resident] = Json.fromJson[Resident](jsonString)
此处residentReads
的对象和类将由Json.reads
后面的宏生成,只要它实现Reads
,您就不会关心它的类型性状。
trait
)的情况。考虑来自ExecutionContextImpl
def fromExecutorService(es: ExecutorService, reporter: Throwable => Unit = ExecutionContext.defaultReporter):
ExecutionContextImpl with ExecutionContextExecutorService = {
new ExecutionContextImpl(Option(es).getOrElse(createDefaultExecutorService(reporter)), reporter)
with ExecutionContextExecutorService {
final def asExecutorService: ExecutorService = executor.asInstanceOf[ExecutorService]
override def execute(command: Runnable) = executor.execute(command)
override def shutdown() { asExecutorService.shutdown() }
override def shutdownNow() = asExecutorService.shutdownNow()
override def isShutdown = asExecutorService.isShutdown
override def isTerminated = asExecutorService.isTerminated
override def awaitTermination(l: Long, timeUnit: TimeUnit) = asExecutorService.awaitTermination(l, timeUnit)
override def submit[T](callable: Callable[T]) = asExecutorService.submit(callable)
override def submit[T](runnable: Runnable, t: T) = asExecutorService.submit(runnable, t)
override def submit(runnable: Runnable) = asExecutorService.submit(runnable)
override def invokeAll[T](callables: Collection[_ <: Callable[T]]) = asExecutorService.invokeAll(callables)
override def invokeAll[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = asExecutorService.invokeAll(callables, l, timeUnit)
override def invokeAny[T](callables: Collection[_ <: Callable[T]]) = asExecutorService.invokeAny(callables)
override def invokeAny[T](callables: Collection[_ <: Callable[T]], l: Long, timeUnit: TimeUnit) = asExecutorService.invokeAny(callables, l, timeUnit)
}
}
只要符合ExecutionContextExecutorService
的合同,呼叫者就不会关心特定类型,特别是我们并不关心它是基于ExecutionContextImpl
而不是Function
而不是java.lang.Runnable
任何其他实施。
实际情况#1和#2(即&#34; 匿名类型的匿名对象&#34;)如果你需要传递某个工作由于某些原因,它不适合简单的ExecutionContextImpl
接口(因为它需要多个生命周期方法或出于历史兼容性原因)。最好的例子是// As per ThreadFactory contract newThread should return `null` if cannot create new thread.
def newThread(runnable: Runnable): Thread =
if (reserveThread())
wire(new Thread(new Runnable {
// We have to decrement the current thread count when the thread exits
override def run() = try runnable.run() finally deregisterThread()
})) else null
。这是Thread
的另一个例子:
Runnable
runnable
类需要deregisterThread
作为要执行的工作,我们希望将static
作为参数包装为另一个将调用static
的参数{1}}最后但我们并不关心对象的名称或其实际类型。
Companion Object的真正目的是什么?
我可以想到使用Companion Objects的几个主要原因。
BigInt
方法或zero
字段。例如,假设您编写了自定义任意精度算术apply
。你会在哪里放置 List.empty
List(1, 2, 3)
等众所周知的常量,以便从外部访问它们?伴侣对象就是答案。这种伴随对象的另一个非常典型的用法是提供一些工厂方法的方法(通常通过new
),例如你可以编写scala.util.Random
没有object Random extends Random
关键字
Random.nextInt
将类和伴随对象定义为scala.concurrent
所以你可以做到
abstract class GenTraversableFactory[CC[X] <: GenTraversable[X] with GenericTraversableTemplate[X, CC]]
extends GenericCompanion[CC] {
private[this] val ReusableCBFInstance: GenericCanBuildFrom[Nothing] = new GenericCanBuildFrom[Nothing] {
override def apply() = newBuilder[Nothing]
}
def ReusableCBF: GenericCanBuildFrom[Nothing] = ReusableCBFInstance
// some other stuff
}
List
包使用了这个想法。考虑例如:object List extends SeqFactory[List] {
/** $genericCanBuildFromInfo */
implicit def canBuildFrom[A]: CanBuildFrom[Coll, A, List[A]] =
ReusableCBF.asInstanceOf[GenericCanBuildFrom[A]]
def newBuilder[A]: Builder[A, List[A]] = new ListBuffer[A]
// some other stuff
}
由canBuildFrom
伴随对象
++
此map
实际上被许多集合方法使用,这些集合方法将它们转换为其他集合,例如val list: List[Int] = Vector(1, 2, 3).map(x => 2 * x)(breakout)
或{{1}}。此外,这个聪明的技巧允许您通过映射到其他集合类型来编写类似的东西:
{{1}}
答案 1 :(得分:1)
那么,如果我们不想重用,为什么Anonymous对象是好的?
因为我们不需要关心为其指定名称。考虑像List("a","b","c")
这样的列表的定义。在这里,我们使用3个匿名对象创建它。我们也可以这样做:
val a = "a"
val b = "b"
val c = "c"
List(a, b, c)
显然,前一种选择更方便使用。
但Companion Object的真正目的是什么?
基本上它保留了不属于具有相同名称的特定类的实例的静态方法。伴随对象通常出现在scala集合中,例如可以轻松创建对象。
考虑使用List("a", "b", "c")
的相同示例。
List实际上是一个抽象类,因此不能简单地以这种方式创建:
new List("a", "b", "c")
(为什么? - 但这是另一个问题)
而List(“a”,“b”,“c”)无论如何都是较短的形式。
所以为了使用那种较短的形式的方法
override def apply[A](xs: A*): List[A]
是在伴随对象List中引入的。
希望现在应该清楚为什么伴侣对象是有用的。
以下是另一个解释伴随对象优点的资源: http://daily-scala.blogspot.com/2009/09/companion-object.html