Actually there's not much in common between extension method and companion object. Extension method is a method and companion object is an object. It's a little weird to compare a method with an object.
Functionality is quite different. Companion object doesn't let you add methods to a type (after the type is defined, without modification of the type). Extension method isn't for class-level ("static", not instance-level) methods and values and isn't for accessing private members.
Both of them exist in Scala 2 and Scala 3.
The code
// Scala 3
extension (c: Circle)
def circumference: Double = c.radius * math.Pi * 2
is similar to
// Scala 2
implicit class CircleOps(c: Circle) {
def circumference: Double = c.radius * math.Pi * 2
}
The code for companion object is the same in Scala 2 and Scala 3.
Maybe you're actually asking where to place a new method.
If a method is an instance-level method you should
- put it to the class/trait if you can modify it, or
- make the method an extension method if you can't.
If a method is a class-level ("static") method you
- should put it to the companion object if you can modify it, or
- can make the method an extension method to companion object if you can't.
You can always consider some third service/helper/manager class as an alternative if you don't need to access private members.
See also
What is the benefit of putting a method inside a case class compared to in its companion object?
Scala what is the difference between defining a method in the class instead on the companion object
What is the difference between class and instance methods?
Sometimes extension methods are used to avoid issues with variance. For example for covariant class MyClass[+T]
we can't define a method accepting T
class MyClass[+T]:
def foo(t: T): Unit = () // doesn't compile: covariant type T occurs in contravariant position in type T of parameter t
We have to make the method generic
class MyClass[+T]:
def foo[S >: T](t: S): Unit = ()
Alternatively we could define an extension method
class MyClass[+T]
extension [T] (mt: MyClass[T])
def foo(t: T): Unit = ()
For example https://github.com/milessabin/shapeless/blob/v2.3.10/core/src/main/scala/shapeless/syntax/hlists.scala#L25-L26
These methods are implemented here and pimped onto the minimal HList
types to avoid issues that would otherwise be
caused by the covariance of ::[H, T]
.