2012-03-14 22 views
84

In Scala vedo una funzionalità come variabile oggetto-privato. Dal mio background Java non molto ricco ho imparato a chiudere tutto (renderlo privato) e aprire (fornire accessor) se necessario. Scala introduce un modificatore di accesso ancora più severo. Dovrei sempre usarlo 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 traprivato [questo] vs privato

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

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

Il secondo è più rigorosa e mi piace, ma usare sempre o solo se ho un forte motivo?

Modificato: Per come la vedo here privata [questo] è solo un po 'di subcase invece di questo posso usare altri modificatori: "pacchetto, di classe o di un oggetto Singleton". Quindi lo lascerò per qualche caso speciale.

+0

https://gist.github.com/twolfe18/5767545 – twolfe18

risposta

50

Non penso che importi troppo, dal momento che qualsiasi modifica toccherà solo una classe in entrambi i casi. Quindi la ragione più importante per preferire private su protected su public non si applica.

Utilizzare private[this] dove le prestazioni sono davvero importanti (dal momento che si otterrà l'accesso diretto al campo anziché i metodi in questo modo). Altrimenti, basta accontentarsi di uno stile così le persone non hanno bisogno di capire perché questa proprietà è private e che uno è private[this].

+40

Per favore, per saperne di più sull'aspetto della prestazione –

+4

@ om-nom-nom In realtà, non c'è molto da dire. JIT dovrebbe in ogni caso incorporare le chiamate al metodo di accesso generate da 'private', quindi l'impatto dovrebbe essere zero o almeno molto molto piccolo. –

+4

Questa risposta è fuorviante, il motivo effettivo è la varianza del sito di dichiarazione (vedere questa risposta: http://stackoverflow.com/a/9727849/445715). –

12

private[this] (equivalent to protected[this]) means that that "y" is only visible to methods in the same instance. For example, you could not reference y on a second instance in an equals method, i.e., "this.y == that.y" would generate a compilation error on "that.y". (source)

modo da poter fare privato [questo] ogni volta che si vuole, ma si può avere qualche problema se è necessario fare riferimento è

+13

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

+0

Si può fare 'this.y == that.y' senza usare né private né private [questo], ho appena provato sia – lisak

96

C'è un caso in cui private[this] è necessario per rendere il codice di compilazione. Questo ha a che fare con un'interazione tra notazione della varianza e variabili mutabili. Si consideri il seguente (inutile) classe:

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 è progettato per contenere un valore opzionale, restituire come opzione e permettere all'utente di chiamare makeEmpty per azzerare il valore (da qui il var). Come affermato, questo è inutile se non quello di dimostrare il punto.

Se si tenta di compilare questo codice con private invece di private[this] fallirà con il seguente messaggio di errore:

error: covariant type T occurs in contravariant position in type Option[T] of value value_= class Holder[+T] (initialValue: Option[T]) {

Questo errore si verifica perché il valore è una variabile mutabile del tipo covariante T (+ T), che è normalmente un problema se non indicato come privato all'istanza con private[this]. Il compilatore ha una gestione speciale nella sua verifica della varianza per gestire questo caso speciale.

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

+1

Posso vedere perché fallisce quando la mutabilità è nel mix, ma perché faccio [ottenere lo stesso errore quando nulla è mutabile] (http://stackoverflow.com/q/29238887/3625)? –

1

Per approfondire il tema delle prestazioni di cui ha parlato Alexey Romanov, ecco alcune delle mie ipotesi. Citazioni dal libro "Programmazione in Scala: una guida dettagliata e completa, seconda edizione" Sezione 18.2:

In Scala, every var that is non-private member of some object implicitly defines a getter and a setter method with it.

Per verificare il lavoro svolto, questo codice causerà errore di compilazione:

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

Scala lamenta error: ambiguous reference to overloaded definition. L'aggiunta della parola chiave override a data_= non aiuta a dimostrare che il metodo è stato generato dal compilatore. L'aggiunta della parola chiave private alla variabile data causerà comunque questo errore di compilazione. Tuttavia, il seguente codice compila bene:

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

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

10

Questo è stato testato utilizzando scala 2.11.5. Si consideri il seguente codice

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 

che compilerà e il lavoro come questo java (1.8) Codice

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 

se si utilizza '[questo]' modificatore il codice qui sotto non sarà la compilazione

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 è il livello di istanza più rigoroso. Significa che 'x' è accessibile solo dall'istanza a cui appartiene. Quindi 'this.x' va bene ma 'other.x' non lo è.

È possibile consultare la sezione 13.5 di "Programmazione in Scala: una guida dettagliata alla guida dettagliata" per ulteriori dettagli sui modificatori di accesso.

+1

La domanda non è chiedere cosa significa "privato [questo]". Nota la prima frase. –

16

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

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

6

Quando si aggiunge l'ambito alla modificatore privato (privato [X]), si comporta effettivamente come una “fino a” X, dove X indica qualche pacchetto racchiude, classe o un oggetto singleton.

Ad esempio, privato [bar], dove bar è un pacchetto significa che ogni caso di ogni classe appartenente al pacchetto bar può accedere qualsiasi membro il modificatore è limitare.

Nel caso di privato [questo], significa che il membro è accessibile solo per ciascuna istanza. Questo diventa più evidente 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 si può vedere, il secondo Foo non hai alcun problema dato che qualsiasi istanza può accedere alla val privato i. Tuttavia, per il primo Foo c'è un errore poiché ogni istanza non può vedere l'i di un'altra istanza.

È una buona pratica scrivere privato [questo] poiché impone una restrizione più grande.

1

Should I always use it by default? Or should I use it only in some specific cases where I need to explicitly restrict changing field value even for objects of the same class? In other words how should I choose between

E 'meglio utilizzare private[this] se si prevede di sincronizzare la variabile.

Ecco un buon esempio dalla scala style guide of the Spark team:

// 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 } 
} 
1

In più il linguaggio di programmazione OOP come Java, campi privati ​​/ metodi fanno sì che questi campi privati ​​/ metodi non sono accessibili al di fuori dalla classe. Tuttavia, istanze/oggetti della stessa classe possono avere accesso ai campi privati ​​degli oggetti usando l'operatore di assegnazione o tramite il costruttore di copie. In Scala, private [this] è object private, che si assicura che qualsiasi altro oggetto della stessa classe non sia in grado di accedere a [membri] privati.

Esempio

1.Without privata [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.using privata [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 privata [questo] fa in modo che il campo è _password accessibile solo con questo.