private [this]とprivate


111

Scalaでは、このような機能をオブジェクトプライベート変数と見なします。あまりリッチでないJavaのバックグラウンドから、必要に応じてすべてを閉じ(プライベートにし)、開く(アクセサを提供する)ことを学びました。Scalaはさらに厳密なアクセス修飾子を導入しています。デフォルトで常に使用する必要がありますか?または、同じクラスのオブジェクトでもフィールド値の変更を明示的に制限する必要がある一部の特定の場合にのみ使用する必要がありますか?つまり、どのように選択すればよいですか

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

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

2番目の方がより厳格で好きですが、常に使用するべきですか、それとも強い理由がある場合にのみ使用するべきですか?

編集:ここに表示されて private[this]いるように、一部のサブケースがあり、代わりにthis「パッケージ、クラス、またはシングルトンオブジェクト」などの他の修飾子を使用できます。だから私はそれをいくつかの特別な場合のために残しておきます。


回答:


59

変更はいずれかの方法で1つのクラスにしか影響しないため、あまり重要ではないと思います。したがって、優先privateするprotectedよりも優先する最も重要な理由publicは当てはまりません。

private[this]パフォーマンスが本当に重要な場合に使用します(この方法ではメソッドの代わりにフィールドに直接アクセスできるため)。それ以外の場合は、1つのスタイルを選択するだけで十分です。そのため、このプロパティが何であるのかprivateそれがなぜなのかを理解する必要はありませんprivate[this]


6
@ om-nom-nom実際、言うことはあまりありません。JITはprivateとにかく生成されたアクセサメソッド呼び出しをインライン化する必要があるため、影響はゼロまたは少なくとも非常に小さいはずです。
Alexey Romanov

9
この回答は誤解を招くものであり、実際の理由は宣言サイトの差異です(この回答を参照してください:stackoverflow.com/a/9727849/445715)。
Andrey Breslav、2014

1
@AndreyBreslav私はこれがあることを同意しない理由。はい、そのようなケースは存在しますが、答えが言うように、それは非常にまれです。
Alexey Romanov

3
うーん。以下のMarek Adamekの答えは、privateではなく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_ =クラスホルダー[+ T]のタイプOption [T]の反変位置で発生します(initialValue:Option [T]){

このエラーが発生するのは、値が共変タイプT(+ T)の可変変数であるためですprivate[this]。コンパイラーは、この特殊なケースを処理するために、その分散検査で特別な処理を行います。

難解ですが、private[this]が必要な場合がありprivateます。


1
可変性が混在しているときに失敗する理由はわかりますが、何も変更できないときに同じエラーが発生するのはなぜですか?
Matt Kantor、2015

34

private var nameのすべてのメソッドclass Dummy(およびそのコンパニオンobject Dummy)からアクセスできます。

private[this] var namethisオブジェクトのメソッドからのみアクセスでき、の他のオブジェクトからはアクセスできませんclass Dummy


18

private [this](protected [this]と同じ)は、「y」が同じインスタンス内のメソッドにのみ表示されることを意味します。たとえば、equalsメソッドの2番目のインスタンスでyを参照することはできません。つまり、「this.y == that.y」は「that.y」でコンパイルエラーを生成します。 (ソース)

だからいつでもプライベート[これ]を行うことができますが、それを参照する必要がある場合は問題が発生する可能性があります


13
private[this]と等しくないprotected[this]protected[this]サブクラスインスタンスがメンバーにアクセスできるようにします。
drexin 2012年

あなたはthis.y == that.yプライベートもプライベートも使用できません[これ]、私は両方を試しました
lisak '29

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」にアクセスできるのに対し、2番目のケースではより厳密なインスタンスレベルだからです。これは、「x」が属するインスタンスからのみアクセスできることを意味します。したがって、「this.x」は問題ありませんが、「other.x」は問題です。

アクセス修飾子の詳細については、「Scalaでのプログラミング:包括的なステップバイステップガイド」の13.5節を参照してください。


1
問題は何をprivate[this]意味するのかということではありません。最初の文に注意してください。
Alexey Romanov

9

スコープをプライベート修飾子(private [X])に追加すると、「最大」のXとして効果的に動作します。Xは、パッケージ、クラス、またはシングルトンオブジェクトを示します。

たとえば、barがパッケージである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

ご覧のとおり、2番目のFooには問題がありません。どのインスタンスもプライベートval iにアクセスできるためです。ただし、最初のFooでは、各インスタンスが他のインスタンスのiを認識できないため、エラーが発生します。

大きな制限を課すため、private [this]と記述することをお勧めします。


6

JavaのようなほとんどのOOPプログラミング言語では、プライベートフィールド/メソッドは、これらのプライベートフィールド/メソッドがクラスの外部からアクセスできないことを意味します。ただし、同じクラスのインスタンス/オブジェクトは、代入演算子またはコピーコンストラクターを使用して、オブジェクトのプライベートフィールドにアクセスできます。Scalaでは、private [this]はオブジェクトプライベートであり、同じクラスの他のオブジェクトがprivate [this]メンバーにアクセスできないようにします。

1.プライベートなし[これ]

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.private [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フィールドがこれでのみアクセス可能であることを確認します。


これは、最も明確で客観的な答えです。
Lucas Lima

2

Alexey Romanovが述べたパフォーマンスの問題について詳しく説明するために、ここに私の推測の一部を示します。本「Scalaでのプログラミング:包括的なステップバイステップガイド、第2版」のセクション18.2からの引用:

Scalaでは、あるオブジェクトの非プライベートメンバーであるすべてのvarは、それを使用してゲッターとセッターメソッドを暗黙的に定義します。

それをテストするために、このコードはコンパイルエラーを引き起こします:

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メソッドを呼び出すオーバーヘッドが節約されます。


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 }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.