приватний [цей] проти приватний


112

У Scala я бачу таку особливість, як об'єктно-приватна змінна. Зі свого не дуже багатого Java-фону я навчився закривати все (робити це приватним) та відкривати (надавати аксесуари), якщо потрібно. Scala представляє ще більш суворий модифікатор доступу. Чи слід завжди використовувати його за замовчуванням? Або я повинен використовувати його лише в деяких конкретних випадках, коли мені потрібно чітко обмежити значення зміни поля навіть для об'єктів одного класу? Іншими словами, як мені вибрати між

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

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

Друга - більш сувора, і мені це подобається, але чи слід я завжди її використовувати або тільки якщо у мене є вагомі причини?

ВИДАЛЕНО: Як я бачу, тут private[this] є лише підрозділ, і замість цього thisя можу використовувати інші модифікатори: "пакет, клас або однотонний об'єкт". Тож я залишу це для якогось особливого випадку.


Відповіді:


59

Я не думаю, що це має велике значення, оскільки будь-які зміни стосуватимуться лише одного класу. Тому найважливіша причина віддавати перевагу privateнад protectedнад publicне стосується.

Використовуйте private[this]там, де продуктивність дійсно важлива (оскільки ви отримаєте прямий доступ до поля замість методів таким чином). В іншому випадку просто зупиніться на одному стилі, щоб людям не потрібно було з'ясовувати, чому це властивість privateі що таке private[this].


6
@ om-nom-nom Насправді сказати не багато. JIT повинен вбудовувати виклики методу доступу до privateбудь- якого випадку, тому вплив має бути нульовим або, принаймні, дуже маленьким.
Олексій Романов

9
Ця відповідь вводить в оману, фактична причина - дисперсія сайту-оголошення (див. Цю відповідь: stackoverflow.com/a/9727849/445715 ).
Андрій Бреслав

1
@AndreyBreslav Я НЕ згоден , що це причина. Так, такий випадок існує, але як йдеться у відповіді, це досить рідко.
Олексій Романов

3
Хм. Відповідь Марека Адамека нижче, здається, є реальною причиною вибору приватного [цього] над приватним. Наміром є обмеження доступу до певного екземпляра, на відміну від усіх екземплярів класу.
Рам Раджамоні

3
@AlexeyRomanov - питання задає "Чи слід завжди використовувати його за замовчуванням?". Я думаю, ви могли б покращити свою відповідь, сказавши, що приватне [це] не можна використовувати, якщо вам потрібне поле з іншого примірника того ж класу.
Рам Раджамоні

130

Є випадок, коли private[this]потрібно зробити компіляцію коду. Це пов'язано з взаємодією позначень дисперсій та змінних змінних. Розглянемо наступний (марний) клас:

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

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

Таким чином, цей клас призначений для зберігання необов'язкового значення, повернення його як опції та надання можливості користувачеві зателефонувати, makeEmptyщоб очистити значення (звідси і var). Як зазначалося, це марно, окрім демонстрації суті.

Якщо ви спробуєте компілювати цей код privateзамість private[this]нього, це не вдасться із таким повідомленням про помилку:

помилка: коваріант типу T зустрічається в противаріантному положенні типу Опція [T] значення value_ = власник класу [+ T] (початкове значення: опція [T]) {

Ця помилка виникає через те, що значення є змінною змінною коваріантного типу T (+ T), яка, як правило, є проблемою, якщо вона не позначена як приватна для екземпляра private[this]. Компілятор має спеціальну оброблюваність у своїй перевірці дисперсії для обробки цього особливого випадку.

Отже, це езотерика, але є випадок, коли private[this]це потрібно private.


1
Я можу зрозуміти, чому він не вдається, коли змінність знаходиться в суміші, але чому я отримую ту ж помилку, коли нічого не змінюється ?
Метт Кантор

34

private var nameє доступним з будь-якого методу class Dummy(та його супутника object Dummy).

private[this] var nameдоступний лише з методів thisоб'єкта, а не з інших об'єктів class Dummy.


18

private [це] (еквівалент захищеному [це]) означає, що "y" видно лише методам у тому самому екземплярі. Наприклад, ви не можете посилатись y на другий примірник методом рівних, тобто "this.y == that.y" генерував би помилку компіляції на "that.y". (джерело)

тож ви можете робити приватне [це] кожен раз, коли захочете, але у вас може виникнути певна проблема, якщо вам потрібно передати це


13
private[this]не дорівнює protected[this]. protected[this]дозволяє екземплярам підкласу отримати доступ до члена.
drexin

Не можна this.y == that.yвикористовувати ні приватне, ні приватне [це], я просто спробував і те, і інше
lisak

12

Це було випробувано за допомогою шкали 2.11.5. Розглянемо код нижче

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

він буде компілювати та працювати як цей 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

однак якщо ви використовуєте модифікатор '[this]', код нижче не буде компілюватися

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

Це тому, що в першому випадку "x" є доступним на рівні класу, тоді як у другому - більш суворий рівень примірника. Це означає, що доступ до "x" може бути доступний лише з того примірника, якому він належить. Отже, "this.x" добре, але "other.x" - ні.

Щоб отримати докладнішу інформацію про модифікатори доступу, ви можете ознайомитись з розділом 13.5 "Програмування в Scala: Всеохоплюючий покроковий посібник".


1
Питання не запитує, що private[this]означає. Зверніть увагу на перше речення.
Олексій Романов

9

Додаючи область до приватного модифікатора ( приватний [X] ), він ефективно поводиться як "до" X, де X позначає якийсь об'єкт, що додає пакет, клас або одиночний об'єкт.

Наприклад, приватний [bar] , де bar є пакетом, означає, що кожен екземпляр кожного класу, що належить до панелі пакунків, може отримати доступ до того члена, який модифікатор обмежує.

У разі приватного [цього] це означає, що член доступний лише для кожного екземпляра. Це стає більш зрозумілим у наступному прикладі:

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

Як бачите, другий Foo не має жодних проблем, оскільки будь-який екземпляр може отримати доступ до приватного val i. Однак для першого Foo є помилка, оскільки кожен екземпляр не може бачити i іншого екземпляра.

Добре писати приватне [це], оскільки воно накладає більші обмеження.


6

У більшості мов програмування OOP, таких як java, приватні поля / методи означають, що ці приватні поля / методи недоступні за межами класу. Однак екземпляри / об'єкти одного класу можуть мати доступ до приватних полів об'єктів за допомогою оператора присвоєння або за допомогою конструктора копій. У Scala приватний [це] є об'єктом приватного, що гарантує, що будь-який інший об’єкт того ж класу не може отримати доступ до приватних [цього] членів.

Приклад

1.Без приватного [цього]

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. Використання приватного [цього]

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

  }
}

Отже, приватне [це] гарантує, що поле _password доступне лише за допомогою цього.


Це, безумовно, найясніша і об'єктивніша відповідь.
Лукас Ліма

2

Щоб детальніше розглянути питання про виставу, про який згадував Олексій Романов, ось деякі мої здогадки. Цитати з книги "Програмування в Scala: всеосяжний покроковий посібник, 2-е видання" Розділ 18.2:

У Scala кожен var, який не є приватним членом якогось об'єкта, неявно визначає за допомогою нього getter та метод setter.

Щоб перевірити це, цей код призведе до помилки компіляції:

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

Скала скаржиться на error: ambiguous reference to overloaded definition. Додавання ключового слова перевизначення data_=не допоможе, повинно довести, що метод генерується компілятором. Додавання privateключового слова до змінної dataвсе одно спричинить цю помилку компіляції. Однак наступний код складається добре:

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

Отже, я думаю private[this], заважатиме Scala генерувати геттерні та сетер-методи. Таким чином, доступ до такої змінної заощадить накладні витрати на виклик методу getter та setter.


1

Чи слід завжди використовувати його за замовчуванням? Або я повинен використовувати його лише в деяких конкретних випадках, коли мені потрібно чітко обмежити значення зміни поля навіть для об'єктів одного класу? Іншими словами, як мені вибрати між

Краще використовувати, private[this]якщо ви плануєте синхронізувати змінну.

Ось хороший приклад із посібника зі стилю Scala команди 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 }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.