Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

private[this] on mutable constructor parameter causing unexpected behavior [duplicate]

Tags:

scala

When I run the code below,

class A(private[this] var i: Int) {
  println("i = " + i)
  i -= 1
  println("After decrement, i = " + i)

  override def toString = i.toString
}
object A extends App {
  val a = new A(1)
  println("a = " + a)
}

I get:

i = 1
After decrement, i = 0
a = 1

If I replace private[this] by private, I get the expected behavior, i.e. the last output line becomes a = 0. I'm using IntelliJ 2016.1.2, Scala plugin version 3.0.6., and scala-sdk-2.11.8.

like image 447
Stephane Bersier Avatar asked May 16 '16 14:05

Stephane Bersier


2 Answers

It's a bug. See SI-6880.

private[this] on a constructor var causes it to mistakenly shadow itself, and you get the original value instead of the accessor you want.

like image 64
Michael Zajac Avatar answered Nov 02 '22 22:11

Michael Zajac


Yes, it's a bug (SI-6165, SI-6880).

Let's explore further. This is the decompiled code for both cases:

private[this]:

class A extends Object {
  <paramaccessor> private[this] var i: Int = _;
  override def toString(): String = scala.Int.box(A.this.i).toString();
  def <init>(i: Int): com.yuval.A = {
    A.this.i = i;
    A.super.<init>();
    scala.this.Predef.println("i = ".+(scala.Int.box(i)));
    i = i.-(1);
    scala.this.Predef.println("After decrement, i = ".+(scala.Int.box(i)));
    ()
  }
};

Here, we see that var i is created for the object and is accessed directly in the code. We see that i is assigned to A.this.i, which is a direct assignment to the field. Later, the mutated value is assigned to i, the method argument, not to A.this.i, the field of class A. The actual value i is being shadowed.

On the contrary, when i is private:

class A extends Object {
  <paramaccessor> private[this] var i: Int = _;
  <accessor> <paramaccessor> private def i(): Int = A.this.i;
  <accessor> <paramaccessor> private def i_=(x$1: Int): Unit = A.this.i = x$1;
  override def toString(): String = scala.Int.box(A.this.i()).toString();
    def <init>(i: Int): com.yuval.A = {
      A.this.i = i;
      A.super.<init>();
      scala.this.Predef.println("i = ".+(scala.Int.box(A.this.i())));
      A.this.i_=(A.this.i().-(1));
      scala.this.Predef.println("After decrement, i = ".+(scala.Int.box(A.this.i())));
      ()
    }
};

Here, we see that i has a getter and setter methods, unlike private[this]. We also see that the decrement is being done on A.this.i_, which is the setter for the field member A.this.i.

like image 23
Yuval Itzchakov Avatar answered Nov 02 '22 22:11

Yuval Itzchakov