4

Assuming no multiple inheritance and no concerns about interoperability with Java, are the two following declarations equal?

sealed trait Foo { def x: Int }
case object Bar extends Foo { val x = 5 }

and

sealed abstract class Foo(val x: Int)
case object Bar extends Foo(5)
user2066880
  • 4,825
  • 9
  • 38
  • 64
  • 2
    Possible duplicate of [What is the advantage of using abstract classes instead of traits?](http://stackoverflow.com/questions/1991042/what-is-the-advantage-of-using-abstract-classes-instead-of-traits) – Lucia Pasarin Oct 05 '15 at 22:40
  • I've read that thread, I'm asking about this specific example from a purely behavioral point of view. – user2066880 Oct 05 '15 at 22:43
  • 1
    I am not a Scala expert, but I would assume they need to be equivalent in the same way that it would be equivalent comparing (for this concrete example) an abstract class and an interface in Java. – Lucia Pasarin Oct 05 '15 at 22:46
  • 1
    note that you need `val x: Int` in abstract class example, otherways it's just unused constructor parameter which results in ` error: value x is not a member of object Bar` when calling `Bar.x` – Łukasz Oct 06 '15 at 09:16
  • @Łukasz I see! Thanks for pointing this out – user2066880 Oct 08 '15 at 02:20

2 Answers2

2

First, some modifications to your code (see below). I dropped the case as it is not relevant here. I also added val in the constructor of Foo2 as x is otherwise not accessible in Bar2.

sealed trait Foo { def x: Int }
object Bar extends Foo { val x = 5 }

sealed abstract class Foo2(val x: Int)
object Bar2 extends Foo2(5)

object Main {
  def main(args: Array[String]) : Unit = {
    println( Bar.x )
    println( Bar2.x )
  }
}

Are the two following declarations equal?

We need to define, what equal means:

  • Equal wrt. general structure: No. A trait is not an abstract class. A trait can have no constructor parameters, while a class can. On the other hand, a class or object (here Bar2) can only derive from one (abstract) class, while it could mix-in multiple traits. A good comprehesion on traits vs. abstract class is given here: http://www.artima.com/pins1ed/traits.html#12.7 if you need to decide on a trait or a class.
  • Equal wrt. byte code: No. Just run javap -v <classfile> to convince yourself
  • Equal wrt. Bar and Bar2 only: Yes. Both can be accessed and used identically. Both are singletons and they expose a member variable x that is readable (but not writable) from outside.

Also the output of scalac -print is quite helpful, to see what is going on:

sealed abstract trait Foo extends Object {
  def x(): Int
};
object Bar extends Object with com.Foo {
  private[this] val x: Int = _;
  <stable> <accessor> def x(): Int = Bar.this.x;
  def <init>(): com.Bar.type = {
    Bar.super.<init>();
    Bar.this.x = 5;
    ()
  }
};
sealed abstract class Foo2 extends Object {
  <paramaccessor> private[this] val x: Int = _;
  <stable> <accessor> <paramaccessor> def x(): Int = Foo2.this.x;
  def <init>(x: Int): com.Foo2 = {
    Foo2.this.x = x;
    Foo2.super.<init>();
    ()
  }
};
object Bar2 extends com.Foo2 {
  def <init>(): com.Bar2.type = {
    Bar2.super.<init>(5);
    ()
  }
};
object Main extends Object {
  def main(args: Array[String]): Unit = {
    scala.this.Predef.println(scala.Int.box(Bar.x()));
    scala.this.Predef.println(scala.Int.box(Bar2.x()))
  };
  def <init>(): com.Main.type = {
    Main.super.<init>();
    ()
  }
}
Martin Senne
  • 5,939
  • 6
  • 30
  • 47
1

No, these are not equivalent and can lead to trouble later with initialization order. I've added exactly the same line of code to each, showing they were never equal code blocks.

sealed trait Foo {
    def x: Int
    val calc = x * 5 / 2  //at the time this runs, x is actually 0
  }
  case object Bar extends Foo {
    val x = 5

  }


  sealed abstract class Foo2(x: Int){
    val calc = x * 5 / 2
  }
  case object Bar2 extends Foo2(5)

  println(Bar.calc)
  println(Bar2.calc)

  //output  0  12 
LaloInDublin
  • 5,379
  • 4
  • 22
  • 26
  • Downvote, as answer is not comprehensive enough. Second, initialization order needs extreme care, but is not relevant in the case, user2066880 has given. – Martin Senne Oct 06 '15 at 10:57