Java has something called static methods
public class Foo {
private int a:
public Foo(a) { this.a = a; }
public int getA() { return a; }
static public String getB() { return "B"; }
}
it is like a method but not attached to an instance
var foo = new Foo(10);
foo.getA(); // method attached to foo, its value depends on foo
Foo.getB(); // method attached to class but not particular instance
These static methods are used for storing globals (not recommended), or stateless functions not depending on object - like e.g. factories and other utilities. These methods can access private/protected members of the instances, and instances can access private/protected static methods - so you can limit visibility as if they were inside the object, even though they aren't (Java's reflection treats their methods as if they had null
as this
).
In Scala we didn't want to have the distinction for static and non-static methods so we decided to do the following:
- create a separate object where such methods could be stored
- make sure that this object could access instances members and vice-versa
- have a similar name to distinct this
object
from other object
s
That's how we arrived at companion object.
class Foo(val a: Int)
object Foo {
val b = "B"
}
val foo = new Foo(10)
foo.a // this is normal method
foo.getClass // this would return Foo
class[Foo] // same as above
Foo.getClass // this would return Foo$
classOf[Foo.type] // same as above
getClass
is method that you can call on any object - since foo
and Foo
have different classes they would return different values for getClass
.
Any value can describe their type with .type
so foo.type
would be the type of foo
variable (Foo
) and Foo.type
would be the type of Foo
object which is companion of Foo
class (and would be called Foo$
in bytecode).
From the reason why companion object exist, it follows that Foo and its companion does not have the same (instance) methods, so they cannot have the same interface, and so they cannot be of the same type.
When it comes to case class
es they just automatically create a companion object (if it doesn't exist) and generate some methods inside it based on their constructor: e.g. apply
, unapply
.
case class Foo(int: Int = 0)
, save it asFoo.scala
, then compile it withscalac Foo.scala
and see the different methods injavap Foo.class
andjavap Foo$.class
. BasicallyclassOf[Foo]
is more about instance methods while companionclassOf[Foo.type]
is more about "static" methods and factories. – AcknowledgeRandom
is an example) in those cases, even if a different type, it is a subtype; just like with all values. – Meleobject
though), 3) class and its companion object would have private access to each others members and 4) implicits defined within companion would be automatically in the scope when looking for the class implicits. – Acknowledgecase class Foo(a: Int)
- ifclassOf[Foo.type]
/Foo.getClass
(this notation mean that you ask for type ofFoo
val
/takeFoo
value and call.getClass
on it, like on any other object, because Foo is an object) would be of the same type asclassOf[Foo]
, what would be the value ofFoo.a
? If they were of the same type then they would have the same methods and properties while they clearly have different use cases. – Acknowledgejava.lang.Object
and methods which are normal instance methods in companion class and have corresponding static methods in normal class' bytecode. Companion is basically a way of making static methods not-static, so that there wouldn't be a special cases, by putting them in a object-bag. But that cannot be achieved when you have only 1 type because how then would you distinct static from non-static? – Acknowledge