20

お互いにプライベートにアクセスできる2つのインスタンスが必要でした。私は当然、そのコンパニオンクラスの唯一のインスタンスへのアクセスを許可するコンパニオンオブジェクトについて考えました。クラス自体をプライベートにしたので、ユーザーはを使用してインスタンスを作成することはできませんnew

object A {
    def apply = dual
    lazy val dual = new A
}

private class A {
    //some irrelevant logic...
}

このコードはコンパイルされません。私は次のようになります。クラスAは、タイプAのエラーの一部として、その定義スコープをエスケープしますが、これは私にはよくわかりません。私の現在の回避策は、クラスが持つべきすべてのメソッド宣言でトレイトを定義し、class Aそのトレイトを拡張することでしたが、 dualはトレイトタイプであり、タイプではありませんclass A

私がここで見逃している理論上の問題は何ですか?なぜこれが禁じられているのですか?

4

2 に答える 2

29

パオロの解決策は良い(+1)ですが、彼はエラーメッセージを説明しなかったので、それを試してみましょう。この問題は、すべてのメソッドに戻り型が必要であるという事実に起因しています。の元の定義applydualオブジェクトを返したclass Aため、両方の暗黙の戻り型はでしたA。これAは、クライアントに表示される必要があることを意味します-他にどのように関数を呼び出したり、?にアクセスしたりできますvalか?さらに、両方(およびその親オブジェクトも)が公開されているため、グローバルに表示されます。ただし、A privateこれは、パッケージの外部に表示されてはならないことを意味することを宣言しました。そのため、コンパイラでは解決できない競合があります。

一般的な規則として、すべてのパラメーターと戻り値のタイプの関数/メンバーは、(少なくとも)参照メンバー自体と同じ範囲の可視性を持っている必要があります*。applyしたがって、この問題を解決する簡単な方法の1つは、との可視性を減らすことdualですprivate。これはコンパイラを満足させますが、あなたは満足しません:-)

解決策は、静的リターンタイプをpublicトレイトに変更することで問題を回避します。これにより、それを参照しているメンバーと同じ可視性が得られます。返されるオブジェクトの動的タイプは引き続きclass A表示されますが、これはクライアントに表示される必要はありません。これは、 「実装ではなく、インターフェイスへのプログラム」という原則の典型的な例です。

この原則を完全に適用するにはclass A、のprivate内部クラスにobject A変換して、同じパッケージ内の他のクラスでもアクセスできないようにすることができることに注意してください。

trait A {
    //...
}

object A {
    def apply: A = dual
    lazy val dual: A = new AImpl

    private class AImpl extends A {
        //some irrelevant logic...
    }

}

*衒学的であるために、囲んでいるクラス/オブジェクトは、次のように、そのメンバーの可視性を低下させる可能性があります。

private class Holder {
  def member = new Hidden
}

private class Hidden

はどこにmemberありますpublicが、それを囲むクラスはprivate、そのメンバーを外界から効果的に隠しています。したがって、コンパイラはここで苦情を出しません。

于 2013-01-14T15:11:45.883 に答える
25

プライベートクラスではなく、プライベートコンストラクターを持つクラスが必要だと思います。

class A private() 
object A {
    def apply = dual
    lazy val dual = new A
}

これで、クラスは外部コードに「表示」されますが、コンパニオンオブジェクトのみがそのインスタンスを作成できます。

于 2013-01-14T14:51:30.330 に答える