prywatny [this] vs prywatny

112

W Scali widzę taką funkcję jak zmienna prywatna obiektu. Z mojego niezbyt bogatego zaplecza Java nauczyłem się zamykać wszystko (uczynić to prywatnym) i otwierać (udostępniać akcesory), jeśli to konieczne. Scala wprowadza jeszcze bardziej restrykcyjny modyfikator dostępu. Czy zawsze powinienem go używać domyślnie? A może powinienem go używać tylko w określonych przypadkach, w których muszę jawnie ograniczyć zmianę wartości pola nawet dla obiektów tej samej klasy? Innymi słowy, jak mam wybierać między

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

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

Drugi jest bardziej rygorystyczny i mi się podoba, ale czy powinienem go używać zawsze, czy tylko z mocnego powodu?

EDYTOWANE: Jak widzę tutaj private[this] jest tylko przypadek podrzędny i zamiast niego thismogę użyć innych modyfikatorów: „pakiet, klasa lub obiekt pojedynczy”. Więc zostawię to na jakiś specjalny przypadek.

Soteryka
źródło

Odpowiedzi:

59

Nie sądzę, żeby to miało zbyt duże znaczenie, ponieważ wszelkie zmiany dotkną tylko jednej klasy. Tak najważniejszy powód do preferowania privateprzez protectedponad publicnie ma zastosowania.

Używaj private[this]tam, gdzie wydajność naprawdę ma znaczenie (ponieważ uzyskasz bezpośredni dostęp do pola zamiast metod w ten sposób). W przeciwnym razie po prostu zdecyduj się na jeden styl, aby ludzie nie musieli zastanawiać się, dlaczego ta właściwość istnieje, privatea co nią jest private[this].

Alexey Romanov
źródło
6
@ om-nom-nom Właściwie nie ma wiele do powiedzenia. JIT powinien wbudować wywołania metod akcesora generowane przez i privatetak, więc wpływ powinien wynosić zero lub co najmniej bardzo mały.
Alexey Romanov
9
Ta odpowiedź jest myląca, prawdziwym powodem jest różnica między deklaracją a witryną (patrz odpowiedź: stackoverflow.com/a/9727849/445715 ).
Andrey Breslav
1
@AndreyBreslav nie zgadzam się, że jest to powód. Tak, taki przypadek istnieje, ale jak podaje odpowiedź, jest to dość rzadkie.
Alexey Romanov
3
Hmm. Poniższa odpowiedź Marka Adamka wydaje się być prawdziwym powodem wyboru tego, co prywatne. Celem jest ograniczenie dostępu do określonej instancji, w przeciwieństwie do wszystkich instancji tej klasy.
Ram Rajamony
3
@AlexeyRomanov - pytanie brzmi „Czy zawsze powinienem go używać domyślnie?”. Myślę, że możesz poprawić swoją odpowiedź, mówiąc, że prywatne [this] nie może być używane, jeśli potrzebujesz pola z innej instancji tej samej klasy.
Ram Rajamony
130

Istnieje przypadek, w którym private[this]wymagane jest skompilowanie kodu. Ma to związek z interakcją notacji wariancji i zmiennych zmiennych. Rozważmy następującą (bezużyteczną) klasę:

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

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

Tak więc ta klasa została zaprojektowana do przechowywania opcjonalnej wartości, zwracania jej jako opcji i umożliwienia użytkownikowi wywołania w makeEmptycelu wyczyszczenia wartości (stąd zmienna). Jak już wspomniano, jest to bezużyteczne, z wyjątkiem wykazania tego.

Jeśli spróbujesz skompilować ten kod za pomocą privatezamiast private[this]niego, zakończy się niepowodzeniem i pojawi się następujący komunikat o błędzie:

błąd: kowariantny typ T występuje w pozycji kontrawariantnej w typie Option [T] o wartości value_ = class Holder [+ T] (initialValue: Option [T]) {

Ten błąd występuje, ponieważ value jest zmienną zmienną w kowariantnym typie T (+ T), co zwykle stanowi problem, chyba że jest oznaczona jako prywatna dla wystąpienia z private[this]. Kompilator ma specjalną obsługę sprawdzania wariancji, aby obsłużyć ten specjalny przypadek.

Jest to więc ezoteryczne, ale jest przypadek, w którym private[this]jest to wymagane private.

denis phillips
źródło
1
Widzę, dlaczego nie działa, gdy w miksie jest zmienność, ale dlaczego pojawia się ten sam błąd, gdy nic nie jest zmienne ?
Matt Kantor,
35

private var namejest dostępny z dowolnej metody class Dummy(i jego towarzyszącej object Dummy).

private[this] var namejest dostępny tylko z metod thisobiektu, a nie z innych obiektów class Dummy.

comonad
źródło
18

private [this] (odpowiednik protected [this]) oznacza, że ​​„y” jest widoczne tylko dla metod w tej samej instancji. Na przykład nie można odwołać się do y w drugiej instancji w metodzie equals, tj. „This.y == that.y” wygenerowałoby błąd kompilacji na „that.y”. (źródło)

więc możesz zrobić to prywatnie za każdym razem, gdy chcesz, ale możesz mieć problem, jeśli będziesz potrzebować skierowania

Pben
źródło
13
private[this]nie jest równe protected[this]. protected[this]umożliwia instancjom podklasy dostęp do elementu członkowskiego.
drexin
Nie możesz this.y == that.yużywać ani prywatnego, ani prywatnego [tego], właśnie wypróbowałem oba
lisak
12

Zostało to przetestowane przy użyciu wersji 2.11.5. Rozważ poniższy kod

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

skompiluje się i będzie działał jak ten kod 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

jednak jeśli użyjesz modyfikatora „[this]”, poniższy kod nie zostanie skompilowany

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

Dzieje się tak, ponieważ w pierwszym przypadku „x” jest dostępne na poziomie klasy, podczas gdy w drugim przypadku jest to bardziej rygorystyczny poziom instancji. Oznacza to, że dostęp do „x” można uzyskać tylko z instancji, do której należy. Więc „this.x” jest w porządku, ale „other.x” już nie.

Więcej informacji na temat modyfikatorów dostępu można znaleźć w sekcji 13.5 książki „Programming in Scala: A Comprehensive Step-by-Step Guide”.

Marek Adamek
źródło
1
Pytanie nie dotyczy tego, co private[this]oznacza. Zwróć uwagę na pierwsze zdanie.
Alexey Romanov
9

Dodając zasięg do prywatnego modyfikatora ( private [X] ), efektywnie zachowuje się on jak „aż do” X, gdzie X wyznacza otaczający pakiet, klasę lub pojedynczy obiekt.

Na przykład private [bar] , gdzie bar jest pakietem, oznacza, że ​​każda instancja każdej klasy należącej do paska pakietu ma dostęp do dowolnego elementu członkowskiego ograniczonego przez modyfikator.

W przypadku prywatnego [this] oznacza to, że członek jest dostępny tylko dla każdej instancji. Staje się to bardziej jasne w następującym przykładzie:

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

Jak widać, drugie Foo nie ma żadnego problemu, ponieważ każda instancja ma dostęp do prywatnej wartości. Jednak w przypadku pierwszego Foo występuje błąd, ponieważ każda instancja nie widzi i.

Dobrą praktyką jest pisanie tego na osobności, ponieważ nakłada to większe ograniczenie.

Humoyun Ahmad
źródło
6

W większości języków programowania OOP, takich jak java, prywatne pola / metody oznaczają, że te prywatne pola / metody nie są dostępne poza klasą. Jednak instancje / obiekty tej samej klasy mogą mieć dostęp do prywatnych pól obiektów za pomocą operatora przypisania lub za pomocą konstruktora kopiującego. W Scali private [this] jest obiektem prywatnym, co zapewnia, że ​​żaden inny obiekt tej samej klasy nie będzie mógł uzyskać dostępu do prywatnych elementów członkowskich [this].

Przykład

1.Bez prywatnego [this]

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.Używanie prywatnego [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

  }
}

Stąd private [this] zapewnia, że ​​pole _password jest dostępne tylko z this.

rafiquenazir
źródło
To zdecydowanie najjaśniejsza i bardziej obiektywna odpowiedź.
Lucas Lima
2

Aby rozwinąć kwestię wydajności, o której wspomniał Aleksiej Romanow, oto niektóre z moich przypuszczeń. Cytaty z książki „Programming in Scala: A Comprehensive Step-by-Step Guide, 2nd Edition” Rozdział 18.2:

W Scali każda zmienna, która nie jest prywatnym składnikiem jakiegoś obiektu, niejawnie definiuje wraz z nim metodę pobierającą i ustawiającą.

Aby to przetestować, ten kod spowoduje błąd kompilacji:

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

Scala narzeka error: ambiguous reference to overloaded definition. Dodanie słowa kluczowego override do data_=nie powinno pomóc w udowodnieniu, że metoda jest generowana przez kompilator. Dodanie privatesłowa kluczowego do zmiennej datanadal spowoduje ten błąd kompilacji. Jednak poniższy kod kompiluje się dobrze:

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

Myślę więc, że private[this]zapobiegnie generowaniu przez scala metod pobierających i ustawiających. Zatem dostęp do takiej zmiennej oszczędza narzut związany z wywołaniem metody pobierającej i ustawiającej.

DXDXY
źródło
1

Czy zawsze powinienem go używać domyślnie? A może powinienem go używać tylko w określonych przypadkach, w których muszę jawnie ograniczyć zmianę wartości pola nawet dla obiektów tej samej klasy? Innymi słowy, jak mam wybierać między

Lepiej jest użyć, private[this]jeśli planujesz zsynchronizować zmienną.

Oto dobry przykład z przewodnika po stylu scala zespołu 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 }
}
Andriy Kuba
źródło