Sunday, December 13, 2009

Danger: Shadowed member values

It is possible (although not recommended) to declare a public property in a super class and have a private value or variable in a subclass:
  1. scala> class X (val i:Int)
  2. defined class X
  3. scala> class Y(i:Intextends X(19) {
  4.      | println(i)
  5.      | }
  6. defined class Y
  7. scala> val y = new Y(100)
  8. 100
  9. y: Y = Y@3e55a58f
  10. scala> y.i
  11. res7: Int = 19

This begs the question: how can the subclass access the superclass instance of i? Let me re-iterate. Just because it is possible this is a bad idea. This pattern seems like it is begging to create bugs. But who knows there is probably some reason out there where this is required.

The way to access the X.i value from Y is to declare which value you want using the syntax this:X :
  1. scala> class Y(i:Intextends X(19) {
  2.      | println("Y.i"+i)              
  3.      | println("X.i"+(this:X).i)
  4.      | }
  5. defined class Y
  6. scala> new Y(39)
  7. Y.i39
  8. X.i19
  9. res8: Y = Y@451dfada

Repeat: Watch out! Because I learned this danger while writing this topic:
  1. scala> class Y(i:Intextends X(19) {
  2.      | println("1. "+i)              
  3.      | println("2. "+this.i)         
  4.      | println("3. "+(this:Y).i)     
  5.      | println("4. "+(this:X).i)     
  6.      | }
  7. defined class Y
  8. scala> new Y(44)
  9. 1. 44
  10. 2. 44
  11. 3. 19  // this is created via (this:Y).i !
  12. 4. 19
  13. res0: Y = Y@338bd37a

It seems that the syntax (this:X).i accesses the public accessor for i. The public access is a method call I think so it always obtains the value for the property. Shocking!

2 comments:

  1. I'm using a nightly build of Scala 2.8 (2.8.0.r19410-b20091106023416). The class Y won't compile without specifying override. I wonder if it's now illegal in Scala 2.8 to shadow members?

    ReplyDelete
  2. I just downloaded the nightly build 2.8.0.r20174-b20091217020115 and it compiles fine. Are you sure you didn't specify Y as follows:

    class Y(val i:Int) extends X(19)

    or

    case class Y(i:Int) extends X(19)

    because either of these will require an override because the i in Y is public.

    ReplyDelete