privato [questo] vs privato


112

In Scala vedo tale funzionalità come variabile privata dell'oggetto. Dal mio background Java non molto ricco ho imparato a chiudere tutto (renderlo privato) e aprire (fornire accessors) se necessario. Scala introduce un modificatore di accesso ancora più rigoroso. Dovrei usarlo sempre di default? O dovrei usarlo solo in alcuni casi specifici in cui ho bisogno di limitare esplicitamente la modifica del valore del campo anche per oggetti della stessa classe? In altre parole come dovrei scegliere tra

class Dummy {
    private var name = "default name"
}

class Dummy {
    private[this] var name = "default name"
}

Il secondo è più severo e mi piace ma dovrei usarlo sempre o solo se ho un motivo forte?

MODIFICATO: Come vedo qui private[this] è solo un sottocaso e invece di thisposso usare altri modificatori: "pacchetto, classe o oggetto singleton". Quindi lo lascio per qualche caso speciale.


Risposte:


59

Non penso che importi troppo, dal momento che qualsiasi modifica toccherà solo una classe in entrambi i casi. Quindi il motivo più importante per preferire privateover protectedover publicnon si applica.

Usa private[this]dove le prestazioni contano davvero (poiché avrai accesso diretto al campo invece di metodi in questo modo). Altrimenti, stabilisciti solo su uno stile in modo che le persone non abbiano bisogno di capire perché questa proprietà è privatee quella è private[this].


6
@ om-nom-nom In realtà, non c'è molto da dire. JIT dovrebbe inline le chiamate al metodo di accesso generate da in privateogni caso, quindi l'impatto dovrebbe essere zero o almeno molto molto piccolo.
Alexey Romanov

9
Questa risposta è fuorviante, il vero motivo è la varianza del sito della dichiarazione (vedere questa risposta: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav

1
@AndreyBreslav Non sono d'accordo che questo sia il motivo. Sì, un caso del genere esiste ma, come dice la risposta, è piuttosto raro.
Alexey Romanov

3
Hmm. La risposta di Marek Adamek di seguito sembra essere la vera ragione per scegliere il privato [questo] rispetto al privato. Lo scopo è limitare l'accesso a un'istanza specifica, al contrario di tutte le istanze della classe.
Ram Rajamony

3
@AlexeyRomanov - la domanda chiede "Devo sempre usarlo di default?". Penso che potresti migliorare la tua risposta dicendo che private [this] non può essere utilizzato se hai bisogno del campo da un'altra istanza della stessa classe.
Ram Rajamony

130

C'è un caso in cui private[this]è necessario compilare il codice. Ciò ha a che fare con un'interazione di notazione della varianza e variabili mutabili. Considera la seguente classe (inutile):

class Holder[+T] (initialValue: Option[T]) {
    // without [this] it will not compile
    private[this] var value = initialValue

    def getValue = value
    def makeEmpty { value = None }
}

Quindi questa classe è progettata per contenere un valore opzionale, restituirlo come opzione e consentire all'utente di chiamare makeEmptyper cancellare il valore (da cui la var). Come affermato, questo è inutile se non per dimostrare il punto.

Se provi a compilare questo codice con privateinvece di private[this]esso fallirà con il seguente messaggio di errore:

errore: il tipo covariante T si verifica nella posizione controvariante nel tipo Option [T] di valore value_ = class Holder [+ T] (initialValue: Option [T]) {

Questo errore si verifica perché value è una variabile mutabile sul tipo covariante T (+ T) che normalmente rappresenta un problema a meno che non sia contrassegnato come privato per l'istanza con private[this]. Il compilatore ha una gestione speciale nel controllo della varianza per gestire questo caso speciale.

Quindi è esoterico ma c'è un caso in cui private[this]è richiesto private.


1
Posso capire perché fallisce quando la mutabilità è nel mix, ma perché ottengo lo stesso errore quando nulla è mutabile ?
Matt Kantor

35

private var nameè accessibile da qualsiasi metodo di class Dummy(e il suo compagno object Dummy).

private[this] var nameè accessibile solo dai metodi di thisobject, non da altri oggetti di class Dummy.


18

private [this] (equivalente a protected [this]) significa che "y" è visibile solo ai metodi nella stessa istanza. Ad esempio, non è possibile fare riferimento a y su una seconda istanza in un metodo uguale, ovvero "this.y == that.y" genererebbe un errore di compilazione su "that.y". (fonte)

quindi puoi fare [questo] privato ogni volta che vuoi ma puoi avere qualche problema se hai bisogno di segnalarlo


13
private[this]non è uguale a protected[this]. protected[this]consente alle istanze della sottoclasse di accedere al membro.
drexin

Non puoi this.y == that.yusare né privato né privato [questo], ho appena provato entrambi
lisak

12

Questo è stato testato usando scala 2.11.5. Considera il codice seguente

class C(private val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => x == other.x
    case _ => false
  }
}

println(new C(5) == new C(5)) //true
println(new C(5) == new C(4)) //false

compilerà e funzionerà come questo codice java (1.8)

class C {
    private int x;

    public C(int x) {
        this.x = x;
    }

    public boolean equals(Object obj) {
        if (obj instanceof C) {
            return ((C) obj).x == x;
        }
        else {
            return false;
        }
    }
}

System.out.println(new C(5).equals(new C(5))); //true
System.out.println(new C(5).equals(new C(4))); //false

tuttavia, se usi il modificatore "[questo]", il codice seguente non verrà compilato

class C(private[this] val x: Int) {
  override def equals(obj: Any) = obj match {
    case other: C => this.x == other.x //problem is here
    case _ => false
  }
}

Questo perché nel primo caso 'x' è accessibile a livello di classe, mentre nel secondo caso è a livello di istanza più rigoroso. Significa che è possibile accedere a "x" solo dall'istanza a cui appartiene. Quindi "this.x" va bene ma "other.x" non lo è.

Puoi fare riferimento alla sezione 13.5 del libro "Programmazione in Scala: una guida completa passo-passo" per maggiori dettagli sui modificatori di accesso.


1
La domanda non è chiedersi cosa private[this]significhi. Nota la prima frase.
Alexey Romanov

9

Quando si aggiunge l'ambito al modificatore private ( private [X] ), si comporta effettivamente come una X "fino a", dove X designa un pacchetto, una classe o un oggetto singleton che li racchiude.

Ad esempio, private [bar] , dove bar è un pacchetto significa che ogni istanza di ogni classe appartenente alla barra dei pacchetti può accedere a qualsiasi membro il modificatore stia limitando.

Nel caso di private [this] , significa che il membro è accessibile solo per ogni istanza. Ciò diventa più chiaro nel seguente esempio:

class Foo(foo:Foo){  
   private[this] val i = 2
   println(this.i + foo.i)
}

>>error: value i is not a member of Foo

class Foo(foo:Foo){  
    private val i = 2
    println(this.i + foo.i)
}

>>defined class Foo

Come puoi vedere, il secondo Foo non ha alcun problema poiché qualsiasi istanza può accedere alla val i privata. Tuttavia per il primo Foo c'è un errore poiché ogni istanza non può vedere l'i dell'altra istanza.

È una buona pratica scrivere in privato [questo] poiché impone una restrizione maggiore.


6

Nella maggior parte dei linguaggi di programmazione OOP come java, campi / metodi privati ​​significano che questi campi / metodi privati ​​non sono accessibili al di fuori della classe. Tuttavia, istanze / oggetti della stessa classe possono avere accesso ai campi privati ​​degli oggetti utilizzando l'operatore di assegnazione o tramite il costruttore di copia. In Scala, private [questo] è un oggetto privato, che assicura che qualsiasi altro oggetto della stessa classe non sia in grado di accedere ai membri privati ​​[questo].

Esempio

1.Senza privato [questo]

object ObjectPrivateDemo {

  def main(args: Array[String]) {
    var real = new User("realUserName", "realPassword")
    var guest = new User("dummyUserName", "dummyPassword")
    real.displayUser(guest)

  }
}

class User(val username:String,val password:String) {
  private var _username=username
  private var _password=password



  def displayUser(guest:User){

         println(" guest username="+guest._username+" guest password="+guest._password)
       guest._username= this._username
    guest._password=  this._password
       println(" guest username="+guest._username+" guest password="+guest._password)


  }
}

2.Uso privato [questo]

class User(val username: String, val password: String) {
  private var _username = username
  private[this] var _password = password



  def displayUser(guest: User) {

    println(this._username)
    println(this._password)

    guest._username = this._username
    // for guest._password it will give this :error  value _password is not member of class User
    guest._password = this._password

  }
}

Quindi private [this] si assicura che il campo _password sia accessibile solo con questo.


Questa è di gran lunga la risposta più chiara e oggettiva.
Lucas Lima,

2

Per approfondire il problema delle prestazioni menzionato da Alexey Romanov, ecco alcune delle mie ipotesi. Citazioni dal libro "Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition" Sezione 18.2:

In Scala, ogni var che è membro non privato di un oggetto definisce implicitamente un metodo getter e un metodo setter con esso.

Per provarlo, questo codice causerà un errore di compilazione:

class PrivateTest{
  var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Scala si lamenta error: ambiguous reference to overloaded definition. L'aggiunta della parola chiave override a data_=non aiuta dovrebbe dimostrare che il metodo è generato dal compilatore. L'aggiunta di una privateparola chiave alla variabile datacauserà comunque questo errore di compilazione. Tuttavia, il codice seguente viene compilato correttamente:

class PrivateTest{
  private[this] var data: Int = 0
  def data_=(x : Int){
    require(x > 0)
    data = x
  }
}

Quindi, immagino private[this]che impedirà a scala di generare metodi getter e setter. Pertanto, l'accesso a tale variabile salverà il sovraccarico di chiamare il metodo getter e setter.


1

Dovrei usarlo sempre di default? O dovrei usarlo solo in alcuni casi specifici in cui ho bisogno di limitare esplicitamente la modifica del valore del campo anche per oggetti della stessa classe? In altre parole come dovrei scegliere tra

È meglio usare private[this]se prevedi di sincronizzare la variabile.

Ecco un buon esempio dalla guida allo stile di scala del team Spark :

// The following is still unsafe.
class Foo {
  private var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}

// The following is safe.
class Foo {
  private[this] var count: Int = 0
  def inc(): Unit = synchronized { count += 1 }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.