Differences between Anonymous, Singleton & Companion Object in Scala

I searched on Google to find and understand a deep immersion in the differences between Anonymous, Singleton and Companion Object in Scala

I found that in scala,

  • an object that does not have a reference name is called an anonymous object. It's good to create an anonymous object if you don't want to reuse it.

  • A Singleton object is an object that is declared using the object keyword instead of a class. Calling methods declared inside a singleton object does not require an object, and there is no static concept. In this way, scala creates a singleton object to provide an entry point for our program to execute.

  • In scala, when you have a class with the same name as Singleton, it is called a companion class, and a singleton object is called a companion object. The companion class and its companion object must be defined in the same source file.

So why is an anonymous object good if we don't want to reuse it? Singleton is very easy to understand, but what is the real purpose of the Companion object? I mean, should a story related to the rule for writing a companion class and companion object be defined in the same source file? Is this the only reason for the Companion Object that we have a class with the same name as a single object?

I suggest that there must be some important reason for these features in Scala. What is the explanation or is there a resource to learn more about scala Objects from?

+5
source share
2 answers

This is a fairly long answer, but I hope it clarifies a few possible use cases.

So why is an anonymous object good if we don't want to reuse it?

I think that unlike the other two, the term “anonymous object” is not defined in the Scala world. I can think of a few things that could be called like this:

  • Some objects that you do not assign to any named variable or field. This can happen in several cases. For example, consider foldLeft in some collection. You want to pass the initial value, but usually you do not need to specify its name, since it is a one-time object. Another case is when such an object contains some part of the logic that you want to use (a kind of strategy template ). Consider the following snippet from a standard ParIterableLike
  def count(p: T => Boolean): Int = { tasksupport.executeAndWaitResult(new Count(p, splitter)) } 

This particular implementation uses the named tasksupport method because it wants it to be configured. But if not for this, perhaps it was something like

  def count(p: T => Boolean): Int = { new ExecutionContextTaskSupport.executeAndWaitResult(new Count(p, splitter)) } 

and new ExecutionContextTaskSupport will be an anonymous object.

  1. Something to be called an anonymous type .

 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) 

Here, the object and class for residentReads will be generated by the macro for Json.reads , and you don't care what type it has as long as it implements the Reads flag.

  • Or if you have a template method that depends on the strategy returned. I. These are cases when all callers need to know about the type, are that it corresponds to a specific interface contract (i.e. Extends a specific trait ). Consider this snippet from 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) } } 

Again, the caller does not care about a particular type if it conforms to the ExecutionContextExecutorService contract, and it doesn’t matter to us that it is based on ExecutionContextImpl , and not on any other implementation.

In fact, cases # 1 and # 2 (i.e., an anonymous object of an anonymous type) are often combined if you need to transfer some part of the work, which for some reason is not suitable for the simple Function interface (or because it requires more than one life cycle method, or for reasons of historical compatibility). A prime example of this is java.lang.Runnable . And here is another example from 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 

The Thread class requires Runnable as part of the work to be done, and we want to wrap Runnable we got as a parameter with another, which will call deregisterThread at the end, but we don’t care about the name of the object or its actual type.

What is the real purpose of the Companion object?

I can think of a few basic reasons for using Companion objects.

  • Something that in the Java world would be a static way or a static field. For example, suppose you are writing arbitrary arithmetic of arbitrary precision BigInt . Where would you place known constants such as zero so that they are accessible from the outside? The defendant is a companion object. Another fairly typical use for such companion objects is to provide some factory methods (usually via apply ), so you can write
  List.empty List(1, 2, 3) 

without the keyword new

  1. You have some kind of class, and you want to provide a shared default instance for it. There is no need for a singleton in terms of the fact that everything is fine with you, creating more instances of this class. For example, scala.util.Random defines both a class and a companion object as
 object Random extends Random 

so that you can only do

 Random.nextInt 
  1. Something that is probably most deservedly called a "companion object." You have a class hierarchy and a piece of logic that needs to be attached to each class in the hierarchy, but not related to the type of classes in the hierarchy. It may seem a bit complicated, but it is not so difficult. The scala.concurrent package uses this idea a lot. Consider, for example:
 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 } 

which several levels to the inheritance tree is implemented using the List of the companion object

 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 } 

This canBuildFrom actually used by many collection methods that convert them to other collections, such as ++ or map . In addition, this clever trick allows you to write something like this when matching with another type of collection:

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

So why is an anonymous object good if we don't want to reuse it?

Because we do not need to worry about giving him a name. Consider a list definition, such as List("a","b","c") . Here we create it with 3 anonymous objects. We could do the same:

 val a = "a" val b = "b" val c = "c" List(a, b, c) 

Obviously, the previous option is more convenient for work.

but what is the real purpose of the companion object?

It basically stores static methods that do not apply to a specific instance of the class with the same name. Companion objects often appear in scala collections, making it easy, for example, to create objects. Consider the same example with List("a", "b", "c") . The list is actually an abstract class, so you cannot just create it this way:

 new List("a", "b", "c") 

(why? - but that's another question) And List ("a", "b", "c") is shorter anyway. Therefore, to use this shorter form, the override def apply[A](xs: A*): List[A] method has been added to the list of companion objects. Hopefully at the moment it should be clear why companion objects are useful.

Here is another resource explaining the benefits of companion objects: http://daily-scala.blogspot.com/2009/09/companion-object.html

+1
source

Source: https://habr.com/ru/post/1274132/


All Articles