Anonymous,Singleton& Scala中的伴随对象

时间:2017-12-17 15:57:04

标签: scala

我在谷歌搜索并发现并深入了解Anonymous,Singleton& Scala中的伴随对象

我在scala中发现了

  • 没有引用名称的对象称为匿名对象。如果您不想进一步重复使用它,最好创建一个匿名对象。

  • Singleton对象是一个使用object关键字而不是类声明的对象。调用在singleton对象中声明的方法并不需要任何对象,也没有静态概念。因此,scala创建了一个单例对象,为程序执行提供了入口点。

  • 在scala中,当你有一个与singleton对象同名的类时,它被称为伴随类,而singleton对象被称为companion对象。伴随类及其伴随对象都必须在同一源文件中定义。

那么,如果我们不想重用,为什么匿名对象是好的呢? Singleton很容易理解,但是Companion Object的真正目的是什么?我的意思是编写伴侣类和伴侣对象的规则背后的故事都必须在同一个源文件中定义? Companion Object的唯一原因是我们有一个与singleton对象同名的类吗?

我想Scala中的这些功能应该有一些重要原因。有什么解释或是否有资源可以从中了解更多有关Scala对象的信息?

2 个答案:

答案 0 :(得分:7)

这是一个很长的答案,但我希望它澄清了一些潜在的使用场景。

  

那么,如果我们不想重用,为什么匿名对象是好的?

我认为与其他两个术语不同的是" 匿名对象"在Scala世界中没有明确定义。我可以想到一些可能被称为的事情:

  1. 您未分配给任何命名变量或字段的某些对象。在某些情况下会发生这种情况。例如,在某些集合上考虑foldLeft。您希望在那里传递初始值,但您通常不需要给它任何名称,因为这是一次性对象。另一种情况是这样的对象包含你想要使用的一些逻辑(一种strategy pattern)。请考虑使用标准ParIterableLike
  2. 中的一部分
      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将是一个匿名对象。

    1. 应该被称为" 匿名类型对象"的东西。

      • 如果您为某些type-classes实施证据,则会经常发生这种情况。请考虑Play-Json
      • 中的此示例
    2. 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的几个主要原因。

      1. Java世界中的某种东西是BigInt方法或zero字段。例如,假设您编写了自定义任意精度算术apply。你会在哪里放置 List.empty List(1, 2, 3) 等众所周知的常量,以便从外部访问它们?伴侣对象就是答案。这种伴随对象的另一个非常典型的用法是提供一些工厂方法的方法(通常通过new),例如你可以编写
      2. scala.util.Random

        没有object Random extends Random 关键字

        1. 你有一些类,你想为它提供一些共享的默认实例。就创建该类的更多实例而言,没有必要使用单例。例如,Random.nextInt 将类和伴随对象定义为
        2. 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  
          
          }
          
          1. 可能最值得称道的东西可能被称为&#34;伴侣对象&#34;。您有一些类层次结构和一段逻辑应该绑定到层次结构中的每个类,但不是层次结构中类的类型。这可能听起来有点复杂,但并不难。 List包使用了这个想法。考虑例如:
          2. 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