2

I would like to have a class B which can be extended with two field by two traits A_1 and A_2. It is important, that class B does not have these two fields by itself, since class B should also be able to be used without these to fields.

My first idea:

trait A_1 { val x: Int }
trait A_2 { val y: Int }
class B

But new B with A_1 with A_2 does not work, since x and y are abstract members and need to be defined in class B.

My second idea:

trait A_1 { var x: Int = _}
trait A_2 { var y: Int = _}
class B

Then one could set the fields after creating a object of B:

val b = new B with A_1 with A_2
b.x = 1
b.y = 2

This works, but is a bit ugly, since the values have to be set afterwards. This construction also makes the class mutable.

My third idea:

class A_1 (val x: Int)
class A_2 (val y: Int)

But class B is not allowed to extend multiple classes, so this idea does not work.

Which possibilites are left to realize this situation ? Is there a better way than idea 2. ?

John Threepwood
  • 15,593
  • 27
  • 93
  • 149
  • The linerarization and all the complications added by vals/vars on traits makes them really bad for property inheritance. Traits are nice when you want to reuse real code, for properties, just define them in your class. – Maurício Linhares Jun 18 '12 at 12:22
  • I understand your point, but in some situations it is necessary to keep the original class clean from additional properties. – John Threepwood Jun 18 '12 at 12:50

2 Answers2

6

You were so close:

scala> trait A1 {val x: Int}
defined trait A1

scala> trait A2 {val y: Int}
defined trait A2

scala> class B
defined class B

scala> new B with A1 with A2 {val x = 4; val y = 2}
res0: B with A1 with A2 = $anon$1@2bc20611
Nicolas
  • 24,509
  • 5
  • 60
  • 66
  • Thank you. Contrary to missingfaktor, you do not create an anonymous class ? If not, what is this construct be called you are using ? – John Threepwood Jun 18 '12 at 13:08
  • 2
    It's also an anonymous class. The difference, as pointed out by missingfaktor is that he use early initialization, while I use a "standard" initialization. The difference is subtle. May be this post will help you understanding it: http://stackoverflow.com/questions/4712468/in-scala-what-is-an-early-initializer – Nicolas Jun 18 '12 at 13:29
  • @JohnThreepwood It does create an anonymous class. You need a class to have a constructor, and you need a constructor to initialize these vals. – Daniel C. Sobral Jun 18 '12 at 13:37
  • Thank you Nicolas and Daniel. If I understood your explanations right, the suggested solution from Nicolas has the possible risk of throwing a NullPointerException (not in this case, but could have when enhancing the traits like in the example in the given link) ? – John Threepwood Jun 18 '12 at 17:54
  • Yes... almost. Actually, before the initialization, you will use the default value: `null`for object, 0 for `Ìnt` and so on. – Nicolas Jun 18 '12 at 18:12
  • Okay, thank you, Nicolas. I see that your solution is also a legitimate one, but when using it, one should be aware of the possible drawbacks. – John Threepwood Jun 18 '12 at 19:00
  • Thanks to both of you, missingfaktor and Nicolas. Both answers helped me a lot. Unfortunately I can only accept one answer (even if both are correct). – John Threepwood Jun 19 '12 at 21:45
4

What Nicolas suggested.

Or:

scala> trait A_1 { val x: Int }
trait A_2 { val y: Int }
class B
defined trait A_1
defined trait A_2
defined class B

scala> new { val x = 3; val y = 9 } with B with A_1 with A_2
res3: B with A_1 with A_2 = $anon$1@18e1b

The feature used here is known as early initialization.

Community
  • 1
  • 1
missingfaktor
  • 90,905
  • 62
  • 285
  • 365
  • Thank you. If I am correct, you are creating an anonymous class (which extends class B, trait A_1, A_2) and which defines the abstract fields of the traits ? – John Threepwood Jun 18 '12 at 13:06
  • @JohnThreepwood, I think the generated code would be same as that with the approach Nicolas suggested. But I am not sure. – missingfaktor Jun 18 '12 at 13:45