加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

Scala中Anonymous,Singleton和Companion对象之间的差异

发布时间:2020-12-16 09:59:00 所属栏目:安全 来源:网络整理
导读:我在谷歌搜索并发现并深入了解Anonymous,Singleton Scala中的伴随对象 我发现在scala中, 没有引用名称的对象称为匿名对象.当您不想进一步重用它时,最好创建匿名对象. Singleton对象是一个使用object关键字而不是类声明的对象.调用在singleton对象中声明的方
我在谷歌搜索并发现并深入了解Anonymous,Singleton& Scala中的伴随对象

我发现在scala中,

>没有引用名称的对象称为匿名对象.当您不想进一步重用它时,最好创建匿名对象.
> Singleton对象是一个使用object关键字而不是类声明的对象.调用在singleton对象中声明的方法并不需要任何对象,也没有静态概念.因此,scala创建一个单例对象,为程序执行提供入口点.
>在scala中,当你有一个与singleton对象同名的类时,它被称为伴随类,而singleton对象被称为companion对象.伴随类及其伴随对象都必须在同一源文件中定义.

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

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

解决方法

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

So,why Anonymous object is good if we don’t want to reuse?

我认为,与其他两个不同,“匿名对象”一词在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))
  }

新的ExecutionContextTaskSupport将是一个匿名对象.

>应该被称为“匿名类型对象”的东西.

>如果您为某些type-classes实施证据,则会经常发生这种情况.从Play-Json开始考虑这个例子

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特性,你就不关心它有什么类型.

>或者,如果您的模板方法取决于返回的某些策略.即,当所有调用者需要知道类型的情况是它匹配某些指定的接口契约(即扩展某些特征)时.考虑来自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]],timeUnit: TimeUnit) = asExecutorService.invokeAny(callables,timeUnit)
      }
    }

只要它满足ExecutionContextExecutorService的约定,调用者就不会关心特定的类型,特别是我们不关心它是基于ExecutionContextImpl而不是任何其他实现.

实际上情况#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的参数,但我们不关心对象的名称或其实际类型.

what’s the real purpose of Companion Object?

我可以想到使用Companion Objects的几个主要原因.

> Java世界中的某些东西将是静态方法或静态字段.例如,假设您编写了自定义任意精度算术BigInt.你会在哪里放置诸如零之类的众所周知的常数,以便从外部访问它们?伴侣对象就是答案.这种伴随对象的另一个非常典型的用法是提供一些工厂方法(通常通过应用)的方法,例如你可以写

List.empty
 List(1,2,3)

没有新的关键字

>您有一些类,并且您想为它提供一些共享的默认实例.就创建该类的更多实例而言,没有必要使用单例.例如,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或map.此外,这个聪明的技巧允许您通过映射到其他集合类型来编写类似的东西:

val list: List[Int] = Vector(1,3).map(x => 2 * x)(breakout)

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读