Home > database >  What is the benefit of putting a method inside a case class compared to in its companion object?
What is the benefit of putting a method inside a case class compared to in its companion object?

Time:09-22

Say I have a case class like this:

final case class Foo(a : String)

and I want to have a method that operates on Foo. Then I can do:

final case class Foo(a : String){

   def m : Int = a.toInt

}

or I could do


object Foo{

   def m (f : Foo) : Int = f.a.toInt

}

Which one is best or what are advantages and disadvantages of each?

CodePudding user response:

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)

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)

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.

CodePudding user response:

Conceptually at least, a method on a class is usually an operation that transforms an instance.

You can picture the lifecycle of an object like this:

A -----> MyType -----> Mytype ----> B

  \___/         \___/        \___/  
    |             |            |
construct    transform   eliminate/consume/fold

It is customary to put constructors in the companion object and transformations in the class definition.

In Scala 3 I found the following pattern useful:

  • Put core / small methods in the class itself
  • Put bigger / less common methods in an extension section.

CodePudding user response:

These two are meant for very different use-cases, so there should not be any comparision between them.

Anything which can be thought of as the "behaviour" of an object instance and is kind of intrinsic to the object is better to be implemented as class / case class methods.

This point is also related to shared behaviours and inheritance. It will be very cumbersome to manage behaviour overrides in inheritance hierarchies using companion objects. This will be pretty much impossible (and very ugly) to manage for anything which not sealed.

It will also allow you to easily refactor your code, in-case you need to abstract out this case class as a trait in future.

Anything which feels like an utility on top of the object instance can go to companion object, factory manager object or some util object.

  • Related