5
votes

I searched in Google to find and understand the deep dive into the differences between Anonymous, Singleton & Companion Object in Scala

I have found that in scala,

  • an object which has no reference name is called anonymous object. It is good to create anonymous object when you don't want to reuse it further.

  • Singleton object is an object which is declared by using object keyword instead by class. No object is required to call methods declared inside singleton object and there is no static concept. So scala creates a singleton object to provide entry point for our program execution.

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

So, why Anonymous object is good if we don't want to reuse? Singleton is very simple to understand but what's the real purpose of Companion Object? I mean what's the story behind the rule of writing companion class and companion object both must defined in a same source file? Is it the only reason of Companion Object that we have a class with same name as singleton object?

I guess there should be some any important reason for these features in Scala. What is the explanation or is there a resource to learn more about the Scala Objects from?

2

2 Answers

8
votes

This is a quite long answer but I hope it clarifies a bit potential usage scenarios.

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

I think that unlike the other two the term "anonymous object" is not well-defined in the Scala world. I can think of several things that might be called so:

  1. Some object that you don't assign to any named variable or field. This can happen in several cases. For example consider foldLeft on some collection. You want to pass the initial value there but you typically don't need to give it any name as this is a disposable object. Another case is when such object contains some piece of logic you want to use (a kind of strategy pattern). Consider following piece from standard ParIterableLike
  def count(p: T => Boolean): Int = {
    tasksupport.executeAndWaitResult(new Count(p, splitter))
  }

This particular implementation uses named method tasksupport because it wants it to be customizable. But if not that, it might have been something like

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

and new ExecutionContextTaskSupport would be an anonymous object.

  1. Something that should be called an "anonymous type object".

    • This happens quite often if you implement an evidence for some type-classes. Consider this example from 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)

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

  • Or if you have a template method that depends on some strategy returned. I.e these are the cases when all the caller needs to know about the type is that it matches some specified interface contract (i.e. extends certain trait). Consider this piece 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 don't care of the specific type as long as it meets the contract of ExecutionContextExecutorService and particularly we don't care that it is based on ExecutionContextImpl rather than any other implementation.

And actually case #1 and #2 (i.e. "an anonymous object of an anonymous type") are often combined if you need to pass somewhere a piece of work that for some reasons doesn't fit simple Function interface (either because it needs more than one life-cycle method or for historical compatibility reasons). The 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 a piece of work to execute and we want to wrap the runnable we've got as a parameter with another one that will call deregisterThread at the end but we don't care about the name of the object or its actual type.

what's the real purpose of Companion Object?

I can think of several major reasons to use Companion Objects.

  1. Something that in Java world would be a static method or a static field. For instance assume you write you custom arbitrary-precision arithmetic BigInt. Where would you put well-known constants such as zero so they would be accessible from the outside? The companion object is the answer. Another quite typical usage for this kind of companion objects is means to provide some factory methods (typically via apply) so for example you can write
 List.empty
 List(1, 2, 3)

without the new keyword

  1. You have some class and you want to provide some shared default instance for it. It is not necessary a singleton in terms that you are quite OK with creating more instances of that class. For example scala.util.Random defines both class and a companion object as
object Random extends Random

so you can do just

Random.nextInt
  1. Something that is probably most deservedly might be called a "companion object". You have some hierarchy of classes and a piece of logic that should be bound to each class in the hierarchy but is not of the type of the classes in the hierarchy. This might sound a bit complicated but is not that hard. The scala.concurrent package employs 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 a few levels down to inheritance tree is implemented by the List 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 is actually used by many collection methods that transform them into other collections such as ++ or map. Moreover this clever trick allows you to write something like this with mapping to other collection type:

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

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

Because we don't need to care about assigning a name to it. Consider definition of the list like List("a","b","c"). Here we create it with 3 anonymous objects. We could do the same with:

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

Obviously the former option is more convinient to work with.

but what's the real purpose of Companion Object?

Basically it keeps static methods which don't belong to a particular instance of class with the same name. Companion objects often appear in scala collections allowing for instance to easily create objects. Consider the same example with List("a", "b", "c"). List is actually an abstract class hence it can't be simply created this way:

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

(why? - but that is another question) And List("a", "b", "c") is shorter form anyway. So in order to use that shorter form a method override def apply[A](xs: A*): List[A] was introduced in a companion object List. Hopefully for now it should be clear why companion objects are useful.

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