2012-03-14 4 views
84

В Scala я вижу такую ​​функцию, как объектно-частная переменная. Из моего не очень богатого Java-фона я научился закрывать все (сделать его приватным) и открывать (предоставлять доступ к ним), если это необходимо. Scala вводит еще более строгий модификатор доступа. Должен ли я всегда использовать его по умолчанию? Или я должен использовать его только в некоторых конкретных случаях, когда мне нужно явно ограничить изменение значения поля даже для объектов того же класса? Другими словами, как я должен выбрать междуprivate [this] vs private

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

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

Второй более строгий, и мне это нравится, но я должен всегда использовать его или только тогда, когда у меня есть веские основания?

Редакция: Как я вижу here частных [это] только некоторые подслучай и вместо этого я могу использовать другие модификаторы: «пакет, класс или одноплодный объект». Поэтому я оставлю это для какого-то особого случая.

+0

https://gist.github.com/twolfe18/5767545 – twolfe18

ответ

50

Я не думаю, что это слишком важно, поскольку любые изменения коснутся только одного класса в любом случае. Поэтому самая важная причина предпочесть private по сравнению с protected над public не применяется.

Использовать private[this], где производительность действительно имеет значение (так как вы получите прямой доступ к полям вместо методов таким образом). В противном случае просто остановитесь на одном стиле, чтобы людям не нужно было выяснять, почему это. Недвижимость private и что 1 is private[this].

+40

Пожалуйста, скажите подробнее об аспект производительности –

+4

@ om-nom-nom На самом деле, говорить нечего. JIT должна в любом случае встраивать вызовы метода доступа, генерируемые 'private', поэтому воздействие должно быть нулевым или, по крайней мере, очень маленьким. –

+4

Этот ответ вводит в заблуждение, фактическая причина - дисперсия объявления-сайта (см. Этот ответ: http://stackoverflow.com/a/9727849/445715). –

12

private[this] (equivalent to protected[this]) means that that "y" is only visible to methods in the same instance. For example, you could not reference y on a second instance in an equals method, i.e., "this.y == that.y" would generate a compilation error on "that.y". (source)

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

+13

'private [this]' не равно 'protected [this]'. 'protected [this]' позволяет экземплярам подкласса обращаться к члену. – drexin

+0

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

96

Там есть случай, когда 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, чтобы очистить значение (отсюда вар). Как было сказано, это бесполезно, за исключением того, чтобы продемонстрировать суть.

Если вы попытаетесь компиляции этого кода с private вместо private[this] он потерпит неудачу со следующим сообщением об ошибке:

error: covariant type T occurs in contravariant position in type Option[T] of value value_= class Holder[+T] (initialValue: Option[T]) {

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

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

+1

. Я вижу, почему он терпит неудачу, когда изменчивость находится в миксе, но почему я [получаю ту же ошибку когда ничего не изменено] (http://stackoverflow.com/q/29238887/3625)? –

1

Для уточнения вопроса о производительности Алексей Романов упомянул, вот некоторые из моих догадок. Цитаты из книги «Программирование в Scala: всеобъемлющее пошаговое руководство, второе издание». Раздел 18.2:

In Scala, every var that is non-private member of some object implicitly defines a getter and a setter method with it.

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

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

Scala жалуется 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.

10

Это было протестировано с использованием 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 

она будет компилироваться и работать как этот Явы (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 

Однако, если вы используете '[это] модификатор ниже код не скомпилируется

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» можно получить только из экземпляра, к которому он принадлежит. Итак, «this.x» в порядке, но «other.x» - нет.

Более подробную информацию о модификаторах доступа вы можете найти в разделе 13.5 книги «Программирование в Scala: всеобъемлющее пошаговое руководство».

+1

Вопрос не в том, что означает 'private [this]'. Обратите внимание на первое предложение. –

16

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

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

6

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

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

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

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 не имеет какие-либо проблем, так как любой экземпляр может получить доступ к закрытым Вэлам я. Однако для первого Foo существует ошибка, так как каждый экземпляр не может видеть i другого экземпляра.

Это хорошая практика, чтобы написать личное [это], поскольку оно налагает большее ограничение.

1

Should I always use it by default? Or should I use it only in some specific cases where I need to explicitly restrict changing field value even for objects of the same class? In other words how should I choose between

Это лучше использовать private[this], если вы планируете синхронизировать переменную.

Вот хороший пример из scala style guide of the Spark team:

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

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

Пример

1.Without частный [это]

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.Using частный [это]

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 поле доступный только с этим.

Смежные вопросы