2016-05-16 4 views
8

Quando eseguo il codice qui sotto,privata [questo] sul parametro del costruttore mutevole causare un comportamento imprevisto

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) 
} 

ottengo:

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

Se sostituisco private[this] da private, ottengo il comportamento previsto , cioè l'ultima linea di uscita diventa a = 0. Sto usando IntelliJ 2016.1.2, versione 3.0.6 del plugin Scala e scala-sdk-2.11.8.

+1

Sì, ho lo stesso comportamento. E no, non posso spiegarlo :( – Dima

+1

Ho appena provato questo ed è un risultato ancora più strano: http://pastebin.com/fKCjnWVZ – Martijn

+0

@Martijn hai avuto il comportamento opposto, fondamentalmente. Probabilmente è a causa dello stesso bug, la cui essenza sembra essere che il codice compilato ha anche un i locale la cui esistenza non è trasparente (vedi le risposte). Nel tuo codice, stai cambiando this.i, che non influenza l'i locale del codice compilato. –

risposta

6

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

Esploriamo ulteriormente. Questo è il codice decompiled per entrambi i casi:

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))); 
    () 
    } 
}; 

Qui, vediamo che var i viene creato per l'oggetto e si accede direttamente nel codice. Vediamo che i è assegnato a A.this.i, che è un'assegnazione diretta al campo. Successivamente, il valore mutato viene assegnato a i, l'argomento metodo, non a A.this.i, il campo della classe A. Il valore attuale i viene ombreggiato.

Al contrario, quando è iprivate:

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()))); 
    () 
    } 
}; 

Qui, si vede che ha un i metodi getter e setter, a differenza private[this]. Vediamo anche che il decremento viene eseguito su A.this.i_, che è il setter per il membro del campo A.this.i.

8

È un bug. Vedi SI-6880.

private[this] su un costruttore var lo induce a erroneamente ombreggiare se stesso, e si ottiene il valore originale invece dell'accessorio desiderato.

Problemi correlati