Частный [это] против частного
В Scala я вижу такую функцию, как объектно-приватная переменная. Из моего не очень богатого Java-фона я научился закрывать все (сделать его приватным) и открывать (предоставлять доступ к ним), если это необходимо. Scala вводит еще более строгий модификатор доступа. Должен ли я всегда использовать его по умолчанию? Или я должен использовать его только в некоторых конкретных случаях, когда мне нужно явно ограничить изменение значения поля даже для объектов того же класса? Другими словами, как выбрать
class Dummy {
private var name = "default name"
}
class Dummy {
private[this] var name = "default name"
}
Второй более строгий, и мне он нравится, но должен ли я всегда использовать его или только если у меня есть веская причина?
EDITED: Как я вижу здесь private [это] - это всего лишь какой-то подзаголовок, и вместо этого я могу использовать другие модификаторы: "package, class или одноэлементный объект". Поэтому я оставлю это для некоторого особого случая.
Ответы
Ответ 1
Я не думаю, что это слишком важно, поскольку любые изменения коснутся только одного класса. Поэтому самая важная причина предпочтения private
над protected
над public
не применяется.
Используйте private[this]
, где производительность действительно имеет значение (так как вы получите прямой доступ к полям вместо методов таким образом). В противном случае просто настройтесь на один стиль, чтобы людям не приходилось выяснять, почему это свойство private
, а другое - private[this]
.
Ответ 2
Существует случай, когда 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]) {
Эта ошибка возникает из-за того, что значение является изменяемой переменной в ковариантном типе T (+ T), которая обычно является проблемой, если не отмечена как закрытая для экземпляра с private[this]
. Компилятор имеет специальную обработку в своей дисперсии для проверки этого специального случая.
Итак, это эзотерика, но есть случай, когда private[this]
требуется над private
.
Ответ 3
private var name
доступен из любого метода class Dummy
(и его компаньона object Dummy
).
private[this] var name
доступен только из методов объекта this
, а не из других объектов class Dummy
.
Ответ 4
private [this] (эквивалентно защищенному [this]) означает, что это "y" равно видны только методам в одном экземпляре. Например, вы могли бы а не ссылку y на второй экземпляр по методу равных, то есть, "this.y == that.y" генерирует ошибку компиляции на "that.y". (источник)
чтобы вы могли делать частные [это] каждый раз, когда хотите, но у вас может быть какая-то проблема, если вам нужно сослаться на него
Ответ 5
Это было протестировано с использованием 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" отлично, но "other.x" не является.
Более подробную информацию о модификаторах доступа можно найти в разделе 13.5 "Программирование в Scala: Всеобъемлющее пошаговое руководство".
Ответ 6
При добавлении области действия к модификатору private (private [X]) он эффективно ведет себя как "до" X, где X обозначает какой-либо охватывающий пакет, class или singleton.
Например, private [bar], где бар является пакетом, означает, что каждый экземпляр каждого класса, принадлежащего к пакету bar, может получить доступ какой член изменяет модификатор.
В случае 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.
Хорошая практика для написания частного [это], поскольку она налагает большее ограничение.
Ответ 7
Чтобы подробнее остановиться на проблеме производительности, о которой говорил Алексей Романов, вот некоторые из моих догадок.
Цитаты из книги "Программирование в Scala: всестороннее пошаговое руководство, второе издание" Раздел 18.2:
В Scala каждый var, не являющийся частным членом какого-либо объекта, неявно определяет метод getter и setter.
Чтобы проверить это, этот код вызовет ошибку компиляции:
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 методов генерации и setter. Таким образом, доступ к такой переменной позволит сэкономить накладные расходы при вызове метода getter и setter.
Ответ 8
Должен ли я всегда использовать его по умолчанию? Или я должен использовать его только в некоторых конкретные случаи, когда мне необходимо явно ограничить изменение поля значение даже для объектов одного класса? Другими словами, как я должен выберите между
Лучше использовать 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 }
}
Ответ 9
В большинстве языков программирования ООП, таких как java, частные поля/методы означают, что эти частные поля/методы недоступны вне класса. Однако экземпляры/объекты одного класса могут иметь доступ к закрытым полям объектов с помощью оператора присваивания или с помощью конструктора копирования. В Scala private [this] является объектом private, что гарантирует, что любой другой объект того же класса не сможет получить доступ к закрытым [этим] членам.
Пример
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 доступно только с этим.