Scalaのprivateおよびprivate [this]修飾子

Scalaでは、通常のプライベートアクセス修飾子に加えて、プライベート[this]修飾子もあります。 これらの2つの修飾子は互いに非常に似ています。 さらに、Javaには単純なプライベートのみがあります。 したがって、単純なプライベートはそれほどプライベートではなく、セキュリティを強化するためにプライベート[this]を使用する必要があるすべての場所で、簡単に混乱や信念を引き起こす可能性があります。 しかし、実際の状況を見てみましょう。

この記事では、簡潔にするために、主にprivate変数という用語を使用します 。 しかし実際には、クラスのプライベートプロパティまたはプライベートクラスメソッドを意味します。

プライベート


通常のプライベートから始めましょう。 それがどのレベルのセキュリティを提供しているか見てみましょう。 これを確認する最も簡単な方法は、例を使用することです。 コードを実行するには、Intellij IDEAのscalaワークシートを使用しました。 Scalaの私のバージョンは2.11.7です。

プライベートフィールドmyPrivateValとこのフィールドの値を返すメソッドを持つクラスWithPrivateがあるとします。

class WithPrivate { private val myPrivateVal = 5 def show = myPrivateVal } 

showメソッドを呼び出してみましょう。

 val a = new WithPrivate a.show 

結果が表示されます。
res0:Int = 5
すべてが期待どおりです- クラス内でプライベートプロパティを使用できます

ここで、クラスの外部からプライベート変数にアクセスしてみましょう。

 val a = new WithPrivate a.myPrivateVal 

コンパイラーはエラーを出します:
エラー:(9、4)クラスWithPrivateの値myPrivateValにA $ A86.this.WithPrivateでアクセスできません
つまり、クラスの外部からプライベート変数にアクセスすることはできません

相続人の行動を確認しましょう。 相続人内のプライベート変数にアクセスしてみましょう。

 class B extends WithPrivate { def usePrivate = { myPrivateVal } } 

エラーが表示されます。
エラー:(8、6)が見つかりません:値myPrivateVal
予想される結果- 相続人でプライベート変数を使用することはできません

相続人のプライベート変数を再定義してみましょう。

 class C extends WithPrivate { override val myPrivateVal = 10 } 

そして再びエラー:
エラー:(22、17)値myPrivateValは何もオーバーライドしません
これはできません。 しかし、オーバーライドを削除した場合:

 class D extends WithPrivate { val myPrivateVal = 10 } val d = new D d.show 

コンソールには以下が表示されます。
res0:Int = 5
つまり、再定義は機能しませんでした。 相続人に同じ名前の変数を作成しました。 したがって、 相続人のプライベート変数もオーバーライドできません。

ご覧のとおり、private修飾子の動作は、Javaでの動作に対応しています。 つまり、通常のプライベートはあまりプライベートではないという意見は間違っています。 通常のprivateは、少なくともJavaのprivateと同じレベルのプライバシーを提供します。

プライベート[これ]


次の行はprivate [this]です。 しかし、検討する前に、そのような記録が一般的に意味するものを思い出したいと思います。

 class A { private[XXX] val someXxxVal = 5 } 

XXXは次のとおりです。

  1. パッケージの名前 。 この場合、XXXパッケージのすべてのクラスはsomeXxxValにアクセスできます。 この機能は、ライブラリ内でのみ表示され、突き出されないプロパティまたはメソッドが必要な場合に、ライブラリ内で使用されることがよくあります。 単体テストでオブジェクトの内部状態を取得することも役立ちます。
  2. クラス/オブジェクトの名前 。 この場合、クラス/オブジェクトXXXはsomeXxxValまでアクセスできます。
  3. これ 。 彼についてさらに。

それでは、プライベート[this]の動作を見てみましょう。 最も簡単な方法は、通常のプライベートにすでに使用したものと同じテストを適用することです。 各ケースを別々にペイントするのではなく、単に一般的なリストを示します。

 class WithPrivateThis { private[this] val myPrivateVal = 5 def show = myPrivateVal } val a = new WithPrivateThis //      a.show //        a.myPrivateVal //        class B extends WithPrivateThis { def usePrivate = { myPrivateVal } } //       class C extends WithPrivateThis { override val myPrivateVal = 10 } //        override class D extends WithPrivateThis { val myPrivateVal = 10 } val d = new D d.show 

このコードを実行すると、これらのテストでまったく同じ結果が表示されることを確認できます。 つまり、これらのテストのprivate [this]は、 通常のprivateと同じ動作を示します 。 しかし、結果が同じであれば、なぜプライベート[この]が必要なのでしょうか?

しかし、JavaではなくScalaがあります


実際、上記のテストはJavaのプライベートな動作に起因するケースのみを対象としています。 しかし、Scalaの場合、これはプライベートを使用するすべてのケースではありません。

次の例を見てみましょう。

 object WithPrivate { def foo = (new WithPrivate).myPrivateVal } class WithPrivate { private val myPrivateVal = 5 } WithPrivate.foo 

ここで、クラスのコンパニオンオブジェクトを作成し、その内部でプライベート変数を使用します。 この例は正常にコンパイルされ、結果が表示されます。
res0:Int = 5

プライベート[this]でも同じことを試してみましょう:

 object WithPrivateThis { def foo = (new WithPrivateThis).myPrivateVal } class WithPrivateThis { private[this] val myPrivateVal = 5 } WithPrivateThis.foo 

この例では、コンパイラは次のことを誓います:
エラー:(13、36)値myPrivateValはA $ A113.this.WithPrivateThisのメンバーではありません
最初の違いがあります-private [this]の場合、コンパニオンオブジェクトで変数を使用できません 。 オブジェクトはmyPrivateValまでアクセスできません。

次に、そのようなケースを見てみましょう。 同じクラスのインスタンスを入力として受け入れるメソッドがクラスにあるとします。 このオブジェクトのプライベート変数を見るとどうなりますか?

 class WithPrivate { private val myPrivateVal = 5 def withThat(that: WithPrivate) = { that.myPrivateVal } } val a = new WithPrivate val b = new WithPrivate b.withThat(a) 

withThatメソッドでは、同じクラス(WithPrivate)の別のインスタンス(その)のプライベート変数に到達しようとします。
このコードを実行すると、予想される結果が表示されます。
res0:Int = 5

この手法は、たとえば同等の用途でよく使用されます。 それを省略すると、WithPrivateクラスのすべてのインスタンスでmyPrivateValが同じになるため、等しい場合は次のようになります。

 override def equals(obj: Any) = obj match { case that: WithPrivatethis.myPrivateVal == that.myPrivateVal case _ ⇒ false } 

プライベート[this]で同じことを試してみましょう:

 class WithPrivateThis { private[this] val myPrivateVal = 5 def withThat(that: WithPrivateThis) = { that.myPrivateVal } } val a = new WithPrivateThis val b = new WithPrivateThis b.withThat(a) 

そして、私たちは何を見ますか? 間違い!
エラー:(21、11)値myPrivateValはA $ A151.this.WithPrivateThisのメンバーではありません
つまり、 コンパイラは、同じクラスの別のオブジェクトのprivate [this]でprivate変数を使用することを許可しません 。 また、WithPrivateクラスについて上記で説明した方法でequalsを実装することはできません。

最後に


前述の2つの例をまとめると、通常のprivateはprivate [ThisClass]と見なすことができます。ThisClassはprivate変数が宣言されているクラスです。 つまり、可視性のレベルは現在のクラス/オブジェクトによって制限されます。 ThisClassのすべてのインスタンスとこのクラスのコンパニオンオブジェクトは、このプライベート変数を参照します。

private [this]は、クラスの特定のインスタンスのレベルでプライバシーと見なされる必要があります。 つまり、クラスの現在のインスタンス内のプライベート変数にのみアクセスできます。 他のクラスインスタンスもコンパニオンオブジェクトも、このようなプライベート変数にアクセスできません。 private [this]は、通常のprivateのより厳密なバージョンであると言えます。

どのような場合にどの修飾子を使用しますか? 私は次のルールに導かれるようにしています:


結論として、私はそれを通常のプロジェクトに追加したいと思います。単純なプライベートはほとんど常に十分です。 コードを慎重に記述する場合、プライベート[this]を完全に省略することができます。 同じクラスのインスタンスを入力として受け入れるメソッドを持つクラスは多くありません。 そして、そのようなメソッドで破壊的なプライベートメソッドを呼び出したり、他のオブジェクトのプライベート状態を変更したりするのは誰ですか? つまり、プライベート[this]を囲むことによって自分を保護しようとするすべての場所で、コードの規律の適切なレベルではこれが役に立たない可能性があります。 同時に、プライベート[this]の積極的な使用は、ライブラリ内で非常に便利です。

Source: https://habr.com/ru/post/J267305/


All Articles