privat [dies] gegen privat

111

In Scala sehe ich eine Funktion als objektprivate Variable. Aufgrund meines nicht sehr umfangreichen Java-Hintergrunds habe ich gelernt, alles zu schließen (privat zu machen) und bei Bedarf zu öffnen (Accessoren bereitzustellen). Scala führt einen noch strengeren Zugriffsmodifikator ein. Sollte ich es immer standardmäßig verwenden? Oder sollte ich es nur in bestimmten Fällen verwenden, in denen ich das Ändern des Feldwerts auch für Objekte derselben Klasse explizit einschränken muss? Mit anderen Worten, wie soll ich wählen

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

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

Das zweite ist strenger und ich mag es, aber sollte ich es immer verwenden oder nur, wenn ich einen starken Grund habe?

BEARBEITET: Wie ich hier sehe ,private[this] ist nur ein Teilfall und stattdessen thiskann ich andere Modifikatoren verwenden: "Paket, Klasse oder Singleton-Objekt". Also lasse ich es für einen besonderen Fall.

Soterisch
quelle

Antworten:

59

Ich denke nicht, dass es zu wichtig ist, da Änderungen nur eine Klasse berühren. So ist der wichtigste Grund zu bevorzugen privateüber protectedüber publickeine Anwendung findet.

Verwenden private[this]Sie diese Option dort, wo die Leistung wirklich wichtig ist (da Sie auf diese Weise direkten Zugriff auf das Feld anstelle von Methoden erhalten). Ansonsten nur siedeln sich auf einen Stil , damit die Leute nicht brauchen , um herauszufinden , warum diese Eigenschaft ist privateund dass man ist private[this].

Alexey Romanov
quelle
6
@ om-nom-nom Eigentlich gibt es nicht viel zu erzählen. JIT sollte die von privateohnehin generierten Accessor-Methodenaufrufe einbinden, sodass die Auswirkung Null oder zumindest sehr, sehr gering sein sollte.
Alexey Romanov
9
Diese Antwort ist irreführend, der eigentliche Grund ist die Abweichung von der Deklarationsstelle (siehe diese Antwort: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav
1
@AndreyBreslav Ich bin nicht der Meinung, dass dies der Grund ist. Ja, ein solcher Fall existiert, aber wie die Antwort sagt, ist er ziemlich selten.
Alexey Romanov
3
Hmm. Die Antwort von Marek Adamek unten scheint der wahre Grund zu sein, privat [dies] gegenüber privat zu wählen. Die Absicht ist, den Zugriff auf eine bestimmte Instanz im Gegensatz zu allen Instanzen der Klasse zu beschränken.
Ram Rajamony
3
@AlexeyRomanov - Die Frage lautet "Soll ich es immer standardmäßig verwenden?". Ich denke, Sie könnten Ihre Antwort verbessern, indem Sie sagen, dass private [this] nicht verwendet werden kann, wenn Sie das Feld einer anderen Instanz derselben Klasse benötigen.
Ram Rajamony
130

Es gibt einen Fall, in private[this]dem Code kompiliert werden muss. Dies hat mit einer Wechselwirkung von Varianznotation und veränderlichen Variablen zu tun. Betrachten Sie die folgende (nutzlose) Klasse:

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

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

Diese Klasse enthält also einen optionalen Wert, gibt ihn als Option zurück und ermöglicht dem Benutzer den Aufruf makeEmptyzum Löschen des Werts (daher der var). Wie bereits erwähnt, ist dies nutzlos, außer um den Punkt zu demonstrieren.

Wenn versuchen Sie diesen Code kompilieren mit , privateanstatt private[this]es mit der folgenden Fehlermeldung fehlschlagen:

Fehler: Kovarianter Typ T tritt in kontravarianter Position im Typ Option [T] des Wertes value_ = Klasseninhaber [+ T] auf (initialValue: Option [T]) {

Dieser Fehler tritt auf, weil value eine veränderbare Variable für den kovarianten Typ T (+ T) ist, die normalerweise ein Problem darstellt, sofern sie nicht für die Instanz mit als privat markiert ist private[this]. Der Compiler hat eine spezielle Behandlung in seiner Varianzprüfung, um diesen speziellen Fall zu behandeln.

Es ist also esoterisch, aber es gibt einen Fall, in dem private[this]es erforderlich ist private.

Denis Phillips
quelle
1
Ich kann sehen, warum es fehlschlägt, wenn die Veränderlichkeit in der Mischung ist, aber warum erhalte ich den gleichen Fehler, wenn nichts veränderlich ist ?
Matt Kantor
34

private var nameist von jeder Methode des class Dummy(und seines Begleiters object Dummy) zugänglich .

private[this] var nameist nur über thisObjektmethoden zugänglich , nicht über andere Objekte von class Dummy.

Comonad
quelle
18

private [this] (entspricht protected [this]) bedeutet, dass "y" nur für Methoden in derselben Instanz sichtbar ist. Beispielsweise könnten Sie in einer equals-Methode nicht auf y in einer zweiten Instanz verweisen, dh "this.y == that.y" würde einen Kompilierungsfehler für "that.y" erzeugen. (Quelle)

Sie können also jedes Mal privat [dies] tun, aber Sie können ein Problem haben, wenn Sie es weiterleiten müssen

Pben
quelle
13
private[this]ist nicht gleich protected[this]. protected[this]Ermöglicht Unterklasseninstanzen den Zugriff auf das Mitglied.
Drexin
Sie können this.y == that.yweder privat noch privat [dies] verwenden, ich habe gerade beides versucht
lisak
12

Dies wurde mit Scala 2.11.5 getestet. Betrachten Sie den folgenden Code

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

Es wird kompiliert und funktioniert als dieser Java (1.8) Code

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

Wenn Sie jedoch den Modifikator '[this]' verwenden, wird der folgende Code nicht kompiliert

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

Dies liegt daran, dass im ersten Fall auf 'x' auf Klassenebene zugegriffen werden kann, während es im zweiten Fall eine strengere Instanzebene ist. Dies bedeutet, dass auf 'x' nur von der Instanz aus zugegriffen werden kann, zu der es gehört. "This.x" ist also in Ordnung, "other.x" jedoch nicht.

Weitere Informationen zu Zugriffsmodifikatoren finden Sie in Abschnitt 13.5 des Buches "Programmieren in Scala: Eine umfassende Schritt-für-Schritt-Anleitung".

Marek Adamek
quelle
1
Die Frage ist nicht, was private[this]bedeutet. Beachten Sie den ersten Satz.
Alexey Romanov
9

Wenn Sie den Bereich zum privaten Modifikator ( privat [X] ) hinzufügen , verhält er sich effektiv wie ein "bis zu" X, wobei X ein umschließendes Paket, eine Klasse oder ein Singleton-Objekt bezeichnet.

Zum Beispiel privaten [bar] , in denen bar ein Paket bedeutet, dass jede Instanz jeder Klasse zu Paket gehör bar zugreifen kann je nachdem , welches Mitglied die Modifikator beschränken ist.

Im Fall von privat [dies] bedeutet dies, dass das Mitglied nur für jede Instanz zugänglich ist. Dies wird im folgenden Beispiel deutlicher:

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

Wie Sie sehen können, hat der zweite Foo kein Problem, da jede Instanz auf den privaten Wert i zugreifen kann. Beim ersten Foo tritt jedoch ein Fehler auf, da nicht jede Instanz das i einer anderen Instanz sehen kann.

Es ist eine gute Praxis, privat [dies] zu schreiben, da dies eine größere Einschränkung darstellt.

Humoyun Ahmad
quelle
6

In den meisten OOP-Programmiersprachen wie Java bedeuten private Felder / Methoden, dass auf diese privaten Felder / Methoden außerhalb der Klasse nicht zugegriffen werden kann. Instanzen / Objekte derselben Klasse können jedoch mithilfe des Zuweisungsoperators oder mithilfe eines Kopierkonstruktors auf die privaten Felder von Objekten zugreifen. In Scala ist private [this] ein Objekt private, wodurch sichergestellt wird, dass kein anderes Objekt derselben Klasse auf private [this] -Mitglieder zugreifen kann.

Beispiel

1. Ohne private [dies]

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.Verwenden Sie private [this]

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

  }
}

Daher stellt private [this] sicher, dass das Feld _password nur damit zugänglich ist.

Rafiquenazir
quelle
Dies ist bei weitem die klarste und objektivere Antwort.
Lucas Lima
2

Um auf das Performance-Problem einzugehen, das Alexey Romanov erwähnt hat, hier einige meiner Vermutungen. Zitate aus dem Buch "Programmieren in Scala: Eine umfassende Schritt-für-Schritt-Anleitung, 2. Auflage" Abschnitt 18.2:

In Scala definiert jede Variable, die nicht privates Mitglied eines Objekts ist, implizit einen Getter und eine Setter-Methode damit.

Zum Testen verursacht dieser Code einen Kompilierungsfehler:

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

Scala beschwert sich über error: ambiguous reference to overloaded definition. Das Hinzufügen eines Überschreibungsschlüsselworts zu data_=nicht hilfreich sollte beweisen, dass die Methode vom Compiler generiert wird. Das Hinzufügen eines privateSchlüsselworts zur Variablen dataführt weiterhin zu diesem Kompilierungsfehler. Der folgende Code wird jedoch problemlos kompiliert:

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

Ich denke also, private[this]wird Scala daran hindern, Getter- und Setter-Methoden zu generieren. Der Zugriff auf eine solche Variable spart somit den Aufwand für den Aufruf der Getter- und Setter-Methode.

DXDXY
quelle
1

Sollte ich es immer standardmäßig verwenden? Oder sollte ich es nur in bestimmten Fällen verwenden, in denen ich das Ändern des Feldwerts auch für Objekte derselben Klasse explizit einschränken muss? Mit anderen Worten, wie soll ich wählen

Es ist besser zu verwenden, private[this]wenn Sie die Variable synchronisieren möchten.

Hier ein gutes Beispiel aus dem Scala Style Guide des Spark-Teams :

// 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 }
}
Andriy Kuba
quelle