Actually you're asking what is the difference between x.m()
and m(x)
. The difference is how method is resolved. There can be different methods with the same name m
.
In the case x.m()
method is resolved dynamically, at runtime (overriding, late binding, subtype polymorphism)
class Foo {
def m(): Unit = println("Foo")
}
class Bar extends Foo {
override def m(): Unit = println("Bar")
}
val x: Foo = new Bar
x.m() // Bar
In the case m(x)
method is resolved statically, at compile time (overloading, early binding, ad hoc polymorphism)
class Foo
class Bar extends Foo
def m(x: Foo): Unit = println("Foo")
def m(x: Bar): Unit = println("Bar")
val x: Foo = new Bar
m(x) // Foo
One of classes Foo
, Bar
can be a case class.
The latter approach ("statical") can be implemented also with type classes rather than overloading
trait DoM[T] {
def m(t: T): Unit
}
def m[T](t: T)(implicit dm: DoM[T]): Unit = dm.m(t)
class Foo
class Bar extends Foo
implicit val fooDoesM: DoM[Foo] = _ => println("Foo")
implicit val barDoesM: DoM[Bar] = _ => println("Bar")
val x: Foo = new Bar
m(x) // Foo