частный [это] vs частный

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 - спрашивается: «Всегда ли использовать по умолчанию?». Я думаю, вы могли бы улучшить свой ответ, сказав, что private [this] нельзя использовать, если вам нужно поле из другого экземпляра того же класса.
Рам Раджамони
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 встречается в контравариантной позиции в типе Option [T] значения value_ = class Holder [+ T] (initialValue: Option [T]) {

Эта ошибка возникает из-за того, что value является изменяемой переменной ковариантного типа T (+ T), что обычно является проблемой, если только не помечено как частное для экземпляра с помощью private[this]. Компилятор имеет специальную обработку проверки дисперсии для обработки этого особого случая.

Так что это эзотерично, но есть случай, когда private[this]требуется больше private.

Денис Филлипс
источник
1
Я понимаю, почему это не удается, когда в миксе присутствует изменчивость , но почему я получаю ту же ошибку, когда ничего не изменяемо ?
Мэтт Кантор,
35

private var nameдоступен из любого метода class Dummy(и его спутника object Dummy).

private[this] var nameдоступен только из методов thisобъекта, но не из других объектов class Dummy.

комонада
источник
18

private [this] (эквивалент protected [this]) означает, что «y» виден только методам в том же экземпляре. Например, вы не можете ссылаться на y во втором экземпляре в методе equals, т.е. «this.y == that.y» вызовет ошибку компиляции для «that.y». (источник)

так что вы можете делать приватные [это] каждый раз, когда захотите, но у вас могут возникнуть проблемы, если вам нужно направить их

Pben
источник
13
private[this]не равно protected[this]. protected[this]позволяет экземплярам подкласса получить доступ к члену.
drexin
Вы не можете this.y == that.yиспользовать ни приватное, ни приватное [это], я только что попробовал оба
lisak
12

Это было протестировано с использованием scala 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 подходит, а другой - нет.

Вы можете обратиться к разделу 13.5 книги «Программирование на Scala: подробное пошаговое руководство» для получения дополнительных сведений о модификаторах доступа.

Марек Адамек
источник
1
Вопрос не в том, что private[this]означает. Обратите внимание на первое предложение.
Алексей Романов
9

При добавлении области видимости к модификатору private ( private [X] ) он эффективно ведет себя как «до» X, где X обозначает некоторый включающий пакет, класс или одноэлементный объект.

Например, private [bar] , где bar - это пакет, означает, что каждый экземпляр каждого класса, принадлежащего bar package, может получить доступ к любому члену, который ограничивает модификатор.

В случае private [this] это означает, что член доступен только для каждого экземпляра. Это становится более понятным в следующем примере:

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 другого экземпляра.

Хорошая практика - писать приватные [this], так как это накладывает более серьезные ограничения.

Хумоюн Ахмад
источник
6

В большинстве языков программирования ООП, таких как java, частные поля / методы означают, что эти частные поля / методы недоступны вне класса. Однако экземпляры / объекты одного класса могут иметь доступ к закрытым полям объектов с помощью оператора присваивания или с помощью конструктора копирования. В Scala private [this] является закрытым объектом, что гарантирует, что любой другой объект того же класса не может получить доступ к закрытым членам [this].

пример

1. Без приватного [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. Использование приватного [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

  }
}

Следовательно, private [this] гарантирует, что поле _password доступно только с этим.

rafiquenazir
источник
Это, безусловно, наиболее ясный и объективный ответ.
Лукас Лима,
2

Чтобы подробнее остановиться на проблеме производительности, упомянутой Алексеем Романовым, вот некоторые из моих предположений. Цитаты из книги «Программирование на Scala: подробное пошаговое руководство, 2-е издание», раздел 18.2:

В Scala каждая переменная, не являющаяся частным членом некоторого объекта, неявно определяет метод получения и метод установки вместе с ним.

Чтобы проверить это, этот код вызовет ошибку компиляции:

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

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

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

Итак, я думаю, private[this]это помешает scala генерировать методы получения и установки. Таким образом, доступ к такой переменной сэкономит накладные расходы на вызов методов получения и установки.

DXDXY
источник
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 }
}
Андрей Куба
источник