16

基本クラスの一部のメソッドに、基本クラスから直接派生したクラスからアクセスできるようにしたいが、派生クラスから派生したクラスからはアクセスできないようにするクラス構造があります。Java言語仕様によれば、継承されたメソッドのアクセス仕様をオーバーライドして、それらをよりパブリックにすることは可能ですが、プライベートにすることはできません。たとえば、これは私がしなければならないことの要点ですが、違法です。

// Defines myMethod
public class Base {
    protected void myMethod() {}
}

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    private void myMethod();
}

// can't access myMethod.
public class DerivedTwo extends DerivedOne {

}

これを達成する方法はありますか?

私がこれをしたい理由を説明するために編集されました:

この場合、クラス構造はデータ処理およびインポート構造です。表形式のデータでいっぱいのテキストファイルを読み込んで解析し、データベースに保存します。

基本クラスは、その一部を処理するデータベースを管理する基本テーブルクラスです。すべてのテーブルタイプに共通するかなりの量の機能が含まれています。データベースに入ると、それらは統一されます。

中間クラスは、解析されるファイル内のテーブルの種類に固有であり、テーブルの解析およびインポートロジックを備えています。基本クラスのデータベースアクセス関数の一部にアクセスする必要があります。

トップレベルクラスはテーブルに固有であり、親クラスが理解できる方法でテーブルのレイアウトを初期化するだけです。また、基本クラスのユーザーは、中間クラスが実行するデータベース固有の機能を表示したり、アクセスしたりする必要はありません。本質的に、私はこれらの関数を基本クラスの1つ上のレベルにのみ公開し、他の誰にも公開したくないのです。

私が例として投稿したコードは違法ですが、同じ目的を達成するための他の手段があるかもしれないので、私は尋ねます。あるかどうか聞いています。

おそらく、隠すことはこれを表現する間違った方法です-私が本当にする必要があるのは、基本クラスにプライベートであるはずのいくつかの機能を、階層の1つ上のレベルのクラスに公開することです。非表示にすることでこれを実現できますが、非表示がどのように問題になるかはわかります。これを行う別の方法はありますか?

4

8 に答える 8

17

あなたが提起した問題の本質は、オブジェクトモデルの概念的な問題を明らかにしていると思います。あなたは、実際にあなたがしなければならないことが「持っている」または「使用している」関係を説明しているのに、さまざまな別々の責任を「ある」関係として説明しようとしています。基本クラスの機能を子クラスから隠したいという事実は、この問題が実際には3層の継承ツリーにマッピングされていないことを示しています。

古典的なORMの問題を説明しているようです。これをもう一度見て、厳密な「is a」継承以外の概念に再マッピングできるかどうかを確認しましょう。問題は技術的なものではなく、概念的なものだと思います。

あなたが言った:

基本クラスは、その一部を処理するデータベースを管理する基本テーブルクラスです。すべてのテーブルタイプに共通するかなりの量の機能が含まれています。データベースに入ると、それらは統一されます。

これはもっと明確かもしれませんが、DB接続と一般的なデータベース操作を管理する必要があるクラスが1つあるようです。単一責任に続いて、私たちはここで完了したと思います。このクラスを拡張する必要はありません。その機能を使用する必要があるクラスに渡す必要があります。

中間クラスは、解析されるファイル内のテーブルの種類に固有であり、テーブルの解析およびインポートロジックを備えています。基本クラスのデータベースアクセス関数の一部にアクセスする必要があります。

ここでの「ミドルクラス」は、データマッパーのように聞こえます。このクラスは前のクラスを拡張する必要はありません。おそらくコンストラクターまたはセッターにインターフェースとして挿入された、そのクラスへの参照を所有する必要があります。

トップレベルクラスはテーブルに固有であり、親クラスが理解できる方法でテーブルのレイアウトを初期化するだけです。また、基本クラスのユーザーは、中間クラスが実行するデータベース固有の機能を表示したり、アクセスしたりする必要はありません。本質的に、私はこれらの関数を基本クラスの1つ上のレベルにのみ公開し、他の誰にも公開したくないのです。

高レベルのクラスがdbスキーマの知識を持っているように見える理由はわかりませんが(少なくとも、「テーブルのレイアウトを初期化する」というフレーズが私に示唆していることです)、最初の2つのクラス間の関係がカプセル化であったかどうか("has a" / "uses a")継承( "is a")の代わりに、これが問題になるとは思わない。

于 2009-12-14T23:42:58.323 に答える
10

いいえ。なぜ仕様を引用してから、仕様の内容とは逆の方法があるかどうかを尋ねるのかわかりません...

おそらく、これを実行したい理由を説明すると、その方法についていくつかの提案を得ることができます

于 2009-12-14T17:13:17.240 に答える
7

メソッドをオーバーライドする場合、メソッドをよりパブリックにすることはできますが、プライベートにすることはできません。なぜ「一般」という言葉を使うのかわかりません

最も制限の少ないものから最も制限の多いものへの順序付けを覚えておいてください。

public<protected<default<private

はい、 " protected"は(修飾子が使用されていない場合)よりも制限の少ないアクセス修飾子defaultであるため、オーバーライドするメソッドをとしてマークするデフォルトのメソッドをオーバーライドできますが、protectedその逆はできません。

Can:protectedメソッドを1で オーバーライドできますpublic

できない:publicメソッドを1で オーバーライドすることはできませんprotected

于 2009-12-14T17:15:07.797 に答える
6

これを行った場合、DerivedTwoの観点からは、DerivedOneはベースにはなりません。代わりに、必要なのはラッパークラスです

//Uses myMethod but keeps it hidden
public class HiddenBase {
    private final Base base = new Base();
    private void myMethod();
    public void otherMethod() {base.otherMethod();}
}

この方法では、ベースの保護されたメソッドにアクセスできません...

于 2009-12-14T17:18:52.873 に答える
4

あなたが説明することは、protectedアクセスクラスの目的に近く、派生クラスはアクセスできますが、他のすべてのクラスはアクセスできません。

基本クラスから継承する場合、これを制御できないと問題が発生する可能性があります。例外をスローすることでメソッドにアクセスできなくなり、継承されたコードをクラスで利用できるようにするには、次のようにスーパーを直接呼び出します。

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
    @Override
    public void myMethod() {
        throw new IllegalStateException("Illegal access to myMethod");
    }

    private void myPrivateMethod() {
        super.myMethod();
    }

}

編集:あなたの詳細に答えるために、私があなたを正しく理解しているなら、あなたは中産階級で定義されている基本クラスの文脈で振る舞いを指定する必要があります。抽象保護されたメソッドは、ミドルクラスから派生したクラスからは見えなくなります。

考えられるアプローチの1つは、基本クラスで抽象化する必要があるメソッドとのインターフェイスを定義し、基本クラスでプライベートの最終参照を保持し、中間クラスのオブジェクトを構築するときに実装への参照を提供することです。

インターフェイスは、ミドルクラス内にネストされた(静的?)に実装されます。私が意味するものは次のようになります:

public interface Specific {
    public void doSomething();
}

public class Base {
    private final Specific specificImpl;

    protected Base(Specific s) {
        specificImpl = s;
    }

    public void doAlot() {

         // ...

         specificImpl.doSomething();

         // ...
    }
}

public class Middle extends Base {

    public Middle() {
        super(new Impl());
    }

    private Impl implements Specific {

        public void doSomething() {

            System.out.println("something done");
        }
    }
}

public class Derived extends Middle {

    // Access to doAlot()
    // No access to doSomething()
}
于 2009-12-14T17:27:44.660 に答える
3

基本クラスを使用できるすべての場所で、そのサブクラスの1つを使用することもできるため、継承は機能します。動作は異なる場合がありますが、APIは異なります。この概念は、リスコフの置換原則として知られています。

メソッドへのアクセスを制限できた場合、結果のクラスは同じAPIを持たず、派生クラスの1つを基本クラスのインスタンスに置き換えることはできず、継承の利点が無効になります。

実際に達成したいことは、インターフェースを使用して実行できます。

interface IBase1 {
}

class Derived1 implements IBase1 {
  public void myMethod() {
  }
}

class Derived2 implements IBase1 {
}

class UseMe {
  public void foo(IBase1 something) {
     // Can take both Derived1 and Derived2
     // Can not call something.myMethod()
  }
  public void foo(Derived1 something) {
     something.myMethod();
  }
  public void foo(Derived2 something) {
    // not something.myMethod()
  }
}
于 2009-12-14T17:30:20.527 に答える
3

可能ですが、少しのパッケージ操作が必要であり、長期にわたって使用するよりも少し複雑な構造になる可能性があります。

次のことを考慮してください。


package a;

public class Base {
    void myMethod() {
        System.out.println("a");
    }
}

package a;

public class DerivedOne extends Base {
    @Override
    void myMethod() {
        System.out.println("b");
    }
}

package b;

public class DerivedTwo extends a.DerivedOne {
    public static void main(String... args) {
        myMethod(); // this does not compile...
    }
}

私はあなた自身、あなたの同僚、そしてあなたのコードを維持しなければならない他の人に親切にすることをお勧めします。これを回避するために、クラスとインターフェースを再考してください。

于 2009-12-14T17:40:54.000 に答える
2

オーバーライドするときは、メソッドをfinalにする必要があります

public class Base {
protected void myMethod() {}
}

// Uses myMethod and then hides it.
public class DerivedOne extends Base {
@Override
final protected void myMethod(); //make the method final
}


public class DerivedTwo extends DerivedOne {
   // can't access myMethod here.
}
于 2015-12-31T05:09:22.167 に答える