user6296589
user6296589

Reputation: 57

Scala: which classes should not have companion objects?

The following pattern seems to be idiomatic scala:

class Foo() {}
object Foo { def apply() = new Foo() }
val x = Foo()

What is the motivation for the idiomatic approach? In which cases should I not provide the factory method, forcing client to use val y = new Foo()? Should all cases with companion objects provide a factory method?

Upvotes: 0

Views: 321

Answers (2)

radumanolescu
radumanolescu

Reputation: 4161

A class does not need a companion object if none of the following apply:

  • There is functionality in the class that is not tied to an instance (what is called "static" in Java)
  • There is a desire to provide a constructor that avoids the need to call new. This is accomplished in Scala by having SomeClass.apply(params)

If none of the above are of interest, there is no need for the companion object. A companion object is a useful structure - if you have those needs. If not, there is no reason to have it. I am not aware of any reason to categorically not have a companion object. It's a tool that you don't write if you don't need to.

Upvotes: 0

som-snytt
som-snytt

Reputation: 39587

Case class apply is normally rewritten by the compiler to new. That's not done if you write a custom apply, even a trivial one.

There are probably similar edge cases around implicit value classes.

$ scala
Welcome to Scala 2.12.3 (OpenJDK 64-Bit Server VM, Java 1.8.0_131).
Type in expressions for evaluation. Or try :help.

scala> case class K(i: Int)
defined class K

scala> K(42)
res0: K = K(42)

scala> :javap -c res0
Compiled from "<console>"
public class $line4.$read$$iw$$iw$ {
  public static $line4.$read$$iw$$iw$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class $line4/$read$$iw$$iw$
       3: invokespecial #28                 // Method "<init>":()V
       6: return

  public $line3.$read$$iw$$iw$K res0();
    Code:
       0: aload_0
       1: getfield      #31                 // Field res0:L$line3/$read$$iw$$iw$K;
       4: areturn

  public $line4.$read$$iw$$iw$();
    Code:
       0: aload_0
       1: invokespecial #33                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #35                 // Field MODULE$:L$line4/$read$$iw$$iw$;
       8: aload_0
       9: new           #14                 // class $line3/$read$$iw$$iw$K
      12: dup
      13: bipush        42
      15: invokespecial #38                 // Method $line3/$read$$iw$$iw$K."<init>":(I)V
      18: putfield      #31                 // Field res0:L$line3/$read$$iw$$iw$K;
      21: return
}

scala> case class K(i: Int) ; object K { def apply(j: Int) = new K(j) }
defined class K
defined object K

scala> K(42)
res1: K = K(42)

scala> :javap -c res1
Compiled from "<console>"
public class $line6.$read$$iw$$iw$ {
  public static $line6.$read$$iw$$iw$ MODULE$;

  public static {};
    Code:
       0: new           #2                  // class $line6/$read$$iw$$iw$
       3: invokespecial #31                 // Method "<init>":()V
       6: return

  public $line5.$read$$iw$$iw$K res1();
    Code:
       0: aload_0
       1: getfield      #34                 // Field res1:L$line5/$read$$iw$$iw$K;
       4: areturn

  public $line6.$read$$iw$$iw$();
    Code:
       0: aload_0
       1: invokespecial #36                 // Method java/lang/Object."<init>":()V
       4: aload_0
       5: putstatic     #38                 // Field MODULE$:L$line6/$read$$iw$$iw$;
       8: aload_0
       9: getstatic     #41                 // Field $line5/$read$$iw$$iw$K$.MODULE$:L$line5/$read$$iw$$iw$K$;
      12: bipush        42
      14: invokevirtual #45                 // Method $line5/$read$$iw$$iw$K$.apply:(I)L$line5/$read$$iw$$iw$K;
      17: putfield      #34                 // Field res1:L$line5/$read$$iw$$iw$K;
      20: return
}

Upvotes: 1

Related Questions