Scala中的匿名,单例和伴随对象之间的区别
我在谷歌搜索了解并深入了解Scala中匿名,单身和伴侣对象之间的差异
我发现在scala中,
没有引用名称的对象称为匿名对象。 当您不想进一步重用它时,创建匿名对象是一件好事。
Singleton对象是通过使用object关键字而不是class来声明的对象。 不需要对象来调用在单例对象中声明的方法,并且没有静态的概念。 所以scala创建了一个单例对象来为我们的程序执行提供入口点。
在scala中,当你有一个与singleton对象具有相同名称的类时,它被称为伴随类,而单例对象称为伴随对象。 伴随类和伴随对象都必须在同一个源文件中定义。
那么,如果我们不想重用,为什么匿名对象是好的? Singleton很容易理解,但Companion Object的真正目的是什么? 我的意思是写作伴侣类和伴侣对象的规则背后的故事都必须在同一个源文件中定义? 它是Companion对象的唯一原因是我们有一个与singleton对象具有相同名称的类吗?
我想在Scala中这些功能应该有一些重要的原因。 什么是解释或有资源来了解更多关于Scala对象?
这是一个相当长的答案,但我希望它澄清了一些潜在的使用场景。
那么,如果我们不想重用,为什么匿名对象是好的?
我认为,与其他两个不同,术语“匿名对象”在Scala世界中没有很好的定义。 我可以想到几个可能会这样调用的东西:
foldLeft
。 你想在那里传递初始值,但你通常不需要给它任何名字,因为这是一个可丢弃的对象。 另一种情况是当这样的对象包含你想要使用的某种逻辑(一种策略模式)时。 考虑从标准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
接口(因为它需要更多的工作)而需要传递给某个地方,实际情况#1和#2(即“匿名类型的匿名对象”比一个生命周期方法或历史兼容性原因)。 这个主要的例子是java.lang.Runnable
。 这是来自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
的参数,但我们不关心对象的名称或它的名称实际类型。
伴侣对象的真正目的是什么?
我可以考虑使用Companion Objects的几个主要原因。
static
方法或static
字段。 例如假设你写自定义的任意精度算术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)
那么,如果我们不想重用,为什么匿名对象是好的?
因为我们不需要关心给它分配一个名字。 考虑像List("a","b","c")
这样的List("a","b","c")
。 在这里我们用3个匿名对象来创建它。 我们可以这样做:
val a = "a"
val b = "b"
val c = "c"
List(a, b, c)
很明显,前一种选择更合适。
但Companion对象的真正目的是什么?
基本上它保留了不属于具有相同名称的特定类实例的静态方法。 伴随对象通常出现在scala集合中,例如可以轻松创建对象。 考虑与List("a", "b", "c")
相同的例子。 List实际上是一个抽象类,因此它不能简单地以这种方式创建:
new List("a", "b", "c")
(为什么? - 但那是另一个问题)而且列表(“a”,“b”,“c”)总是较短。 所以为了使用这个简短形式的方法override def apply[A](xs: A*): List[A]
被引入了一个伴随对象List。 希望现在应该清楚为什么伴侣对象是有用的。
以下是另一个解释伴侣对象好处的资源:http://daily-scala.blogspot.com/2009/09/companion-object.html
链接地址: http://www.djcxy.com/p/76535.html上一篇: Differences between Anonymous, Singleton & Companion Object in Scala