私人[this] vs私人


111

在Scala中,我看到了诸如对象私有变量之类的功能。在我不太丰富的Java背景下,我学会了关闭所有内容(将其设为私有)并在必要时打开(提供访问器)。Scala引入了更严格的访问修饰符。我是否应该始终默认使用它?还是仅在某些情况下才需要使用它,即使对于同一类的对象,我也需要明确限制更改字段值?换句话说,我应该如何选择

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

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

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

编辑:正如我在这里 看到private[this]的只是一些子情况,而不是this我可以使用其他修饰符:“包,类或单例对象”。因此,我将保留一些特殊情况。


Answers:


59

我认为这没什么大不了的,因为任何更改只会以一种方式触及一个类。因此,优先privateprotectedpublic不是的最重要原因并不适用。

private[this]在性能真正重要的地方使用(因为您将获得直接的现场访问而不是这种方式的方法)。否则,只需确定一种样式,这样人们就不必弄清楚为什么属性是,private那个属性是private[this]


6
@ om-nom-nom实际上,没有太多要说的。JIT应该内联private无论如何生成的访问器方法调用,因此影响应该为零或至少非常小。
阿列克谢·罗曼诺夫

9
该答案具有误导性,实际原因是声明位置的差异(请参阅此答案:stackoverflow.com/a/9727849/445715)。
安德烈·布雷斯拉夫

1
@AndreyBreslav我不同意,这是原因。是的,这种情况确实存在,但是正如答案所说的那样,这种情况很少见。
阿列克谢·罗曼诺夫

3
嗯 Marek Adamek在下面的回答似乎是选择private [this]而非private的真正原因。目的是限制对特定实例的访问,而不是该类的所有实例。
Ram Rajamony 2015年

3
@AlexeyRomanov-问题询问“默认情况下我应该一直使用吗?”。我认为您可以通过说private [this]如果您需要来自同一类的另一个实例的字段来改进答案。
Ram Rajamony 2015年

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出现在value_ =类Holder [+ T]的Option [T]类型的协变位置中(initialValue:Option [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 [this](等效于protected [this])表示“ y”仅对同一实例中的方法可见。例如,您不能在equals方法中的第二个实例上引用y,即,“ this.y == that.y”会在“ that.y”上生成编译错误。 (资源)

因此您可以在每次需要时进行private [this],但是如果需要引用它可能会遇到一些问题


13
private[this]不等于protected[this]protected[this]允许子类实例访问成员。
drexin 2012年

您不能this.y == that.y使用private或private [this],我都尝试过这两种方法
lisak 2014年

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”很好,但“ other.x”却不行。

您可以参考“在Scala中编程:全面的循序渐进指南”一书的13.5节,以获取有关访问修饰符的更多详细信息。


1
问题不是在问什么private[this]意思。注意第一句话。
阿列克谢·罗曼诺夫

9

在将范围添加到private修饰符(private [X])时,它实际上表现为“最多” X,其中X指定一些封闭的包,类或单例对象。

例如,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,因此会出现错误。

编写private [this]是个好习惯,因为它施加了更大的限制。


6

在像Java这样的大多数OOP编程语言中,私有字段/方法意味着无法从类外部访问这些私有字段/方法。但是,同一类的实例/对象可以使用赋值运算符或通过复制构造函数访问对象的私有字段。在Scala中,private [this]是私有对象,这可确保同一类的任何其他对象都无法访问private [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字段只能通过this进行访问。


到目前为止,这是最明确,更客观的答案。
卢卡斯·利马

2

为了详细介绍Alexey Romanov提到的性能问题,这是我的一些猜测。引用书“在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生成getter和setter方法。因此,访问此类变量将节省调用getter和setter方法的开销。


1

我是否应该始终默认使用它?还是仅在某些情况下才需要使用它,即使对于同一类的对象,我也需要明确限制更改字段值?换句话说,我应该如何选择

private[this]如果计划同步变量,则最好使用。

这是Spark团队scala风格指南中的一个很好的示例:

// 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 }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.