私人[this] vs私人

时间:2012-03-14 09:02:58

标签: scala

在Scala中,我将此功能视为对象私有变量。从我不太丰富的Java背景中,我学会了关闭所有内容(使其成为私有)并在必要时打开(提供访问者)。 Scala引入了更严格的访问修饰符。我应该总是默认使用它吗?或者我应该只在某些特定情况下使用它,我需要明确限制更改字段值,即使对于同一类的对象?换句话说,我应该如何选择

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

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

第二个更严格,我喜欢它,但是我应该经常使用它还是只有在我有充分理由的情况下?

编辑:我看到here private[this]只是一些子例,而不是this我可以使用其他修饰符:“package,class或singleton object”。所以我会留下一些特殊情况。

9 个答案:

答案 0 :(得分:119)

有一种情况需要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出现在值value_ =类型为Option [T]的逆变位置       class Holder [+ T](initialValue:Option [T]){

发生此错误是因为value是协变类型T(+ T)上的可变变量,除非使用private[this]标记为私有,否则通常会出现问题。编译器在其方差检查中有特殊处理来处理这种特殊情况。

所以这是深奥的,但有一种情况是private[this]需要private

答案 1 :(得分:59)

我认为这不重要,因为任何改变都只会触及一个班级。因此,private优于protected优于public的最重要原因不适用。

使用性能真正重要的private[this](因为您将获得直接字段访问而不是这种方式)。否则,只需确定一种风格,这样人们就不需要弄清楚为什么属性为private 属性为private[this]。< / p>

答案 2 :(得分:27)

可以通过private var name(及其随附class Dummy)的任何方法访问

object Dummy

private[this] var name只能从this对象的方法访问,而不能从class Dummy的其他对象访问。

答案 3 :(得分:16)

  

private [this](相当于protected [this])意味着“y”是   仅对同一实例中的方法可见。例如,你可以   不是在等于方法的第二个实例上引用y,即   “this.y == that.y”会在“that.y”上生成编译错误。   (source)

因此您可以在每次需要时私下[this]但如果您需要参考它可能会遇到问题

答案 4 :(得分: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

但是如果您使用&#39; [this]&#39;修饰符下面的代码不会编译

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

这是因为在第一种情况下&#39; x&#39;在类级别可访问,而在第二种情况下,它是更严格的实例级别。这意味着&#39; x&#39;只能从它所属的实例访问。所以&#39; this.x&#39;很好但是其他的。&#39;不是。

您可以参考&#34; Scala编程:综合循序渐进指南&#34;中的第13.5节;有关访问修饰符的更多详细信息,请参阅。

答案 5 :(得分:7)

将范围添加到私有修饰符(私有[X] )时,它实际上表现为“最多”X,其中X表示一些封闭包, class或singleton对象。

例如, private [bar] ,其中 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,所以会出现错误。

写私人[this]是一个好习惯,因为它会施加更大的限制。

答案 6 :(得分:3)

在大多数OOP编程语言(如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

  }
}

因此私有[this]确保只有_password字段才能访问。

答案 7 :(得分:2)

详细说明Alexey Romanov提到的性能问题,以下是我的一些猜测。 书中的“Scala编程:综合循序渐进指南,第2版”第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。将覆盖关键字添加到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方法。因此,访问这样的变量将节省调用getter和setter方法的开销。

答案 8 :(得分:1)

  

我应该在默认情况下始终使用它吗?或者我应该只在某些地方使用它   我需要明确限制更改字段的特定情况   甚至对于同一类的对象的值?换句话说,我应该怎么做   

之间选择

如果您计划同步变量,最好使用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 }
}