Differences between Anonymous, Singleton & Companion Object in Scala
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?
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:
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.
Something that should be called an "anonymous type object".
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.
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 (ie "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.
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
scala.util.Random
defines both class and a companion object as object Random extends Random
so you can do just
Random.nextInt
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)
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
链接地址: http://www.djcxy.com/p/76536.html