5

これは奇妙なことのように思えるかもしれませんが、サブクラスがメソッドをオーバーライドできるようにしながら、サブクラスが新しいメソッド (コンストラクターを含む) を追加するのを止める方法は Java にあるのでしょうか?

実際の状況は、abstractいくつかの抽象メソッドとコンストラクターを持つクラスがある場所です

abstract class A {
  abstract A doX();
  abstract boolean isY();
  public A(String s){ ... };
}

そして、このクラスの具象サブクラスはすべて、これらのメソッドとコンストラクターのみをオーバーライドする必要があります。

これは、コードに特定のスタイルを強制することです。つまり、コードに取り組んでいる他の人が何かを追加するのを止めます。やめるように言うこともできましたが、それがうまくいくことはめったにありません。

明らかに、クラスを にすることはできませんfinal。効率は最優先事項ではありません。よりクリーンなコードがより重要です。

更新 - 動的アプローチ

回答で指摘されているように、サブクラスの作成を防ぐ唯一の方法は を使用することであるため、これを静的に行う方法はありませんが、これは機能しfinalません。しかし、動的なアプローチを使用できるので、現在の解決策はこれaspectをプロジェクトに追加することです (既に を使用していますAspectJ)。

   public aspect WatchA{
      before() : execute(* A.*()) || execute(* A.*(..)) {
         String methodCalled = joinPoint.getSignature().getName();
         Class<?> c = Class.forName(args[0])
         Method[] allMethods = c.getDeclaredMethods();
         boolean found = false;
         for(Method m : allMethods)
           found |= m.getName().equals(methodCalled);
         if(!found) 
           throw new RuntimeException("Do not add method "+methodCalled+" to A");
      }
   }

これらの新しい方法のいずれかを使用すると、テストが失敗する原因になります。

4

4 に答える 4

8

それをしてはいけない。クラスが存在する場合にのみ、finalサブクラスを作成できないことを保証できます。

メソッドをfinal (抽象クラスであっても) にして、メソッドのオーバーライドを禁止することもできます。

最善の策は、表示したいすべてのメソッドを含むインターフェイスを作成し、API のすべてのユーザーがこのインターフェイスを介してオブジェクトにアクセスするように強制することです。このように、実装が独自のものを追加しても、そのようなものは表示されません。

これに対する 1 つの解決策は、具体的なクラスを返すファクトリを実装することです。「追加のセキュリティ」のために、すべての実装をこのファクトリと同じパッケージに入れ、コンストラクタ パッケージをローカルにすることができます (ただし、これは多くの場合、実用的ではありません)。

public final class MyFactory
{
    // ....
    public MyInterface getConcrete()
    {
        return new MyInterfaceImpl();
    }
    // etc etc -- getStones(), getTar(), getFeathers() and so on
}

ビルダーもそのために使用できることに注意してください。

于 2013-06-17T11:45:07.017 に答える
2

そのような方法はありません。なぜそのようなコーディングスタイルを強制したいのですか?

そのようなスタイルを本当に強制する必要がある場合は、クラスパスをチェックし、抽象親クラスのメソッドをそのサブクラスと比較する「ルール エンフォーサ」を作成できます。

于 2013-06-17T11:44:51.807 に答える
2

本当にこれをしたくない場合.. 1つの方法は、クラスで定義されたメソッドが許可されているメソッドであることを抽象クラスコンストラクターでプログラムでチェックすることです。

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public abstract class Base {

    private static final Set<String> allowedMethodNames = new HashSet<>(Arrays.asList("doThis", "wait", "wait", "wait", "equals", "toString", "hashCode", "getClass", "notify", "notifyAll"));

    public Base() {
        Set<String> allMethods = new HashSet<>();
        for (Method aMethod : getClass().getMethods()) {
            allMethods.add(aMethod.getName());
        }
        if (!allowedMethodNames.equals(allMethods)) {
            allMethods.removeAll(allowedMethodNames);
            throw new IllegalStateException("Following methods not allowed <" + allMethods + ">");
        }
    }

    public abstract void doThis();
}

public class Disallowed extends Base {

    @Override
    public void doThis() {
        System.out.println("dooooooo");
    }

    public void doSomethingElse() {
        System.out.println("not allowed");
    }

    public static void main(String[] args) {
            new Allowed().doThis();
        new Disallowed();
    }

}

public class Allowed extends Base {

    @Override
    public void doThis() {
        System.out.println("doing this");
    }


}

誰かが「Disallowed」のインスタンスを作成しようとすると、失敗します。ただし、「new Allowed().doThis()」は問題なく動作します。

これを行うためのより適切な方法は、カスタム アノテーション + アノテーション プロセッサを導入し、コンパイル時に同じチェックを行うことです。

于 2013-06-17T12:05:49.917 に答える
1

柔軟性を意味するJavaです。そのため、Java を使用すると、抽象メソッドを使用してサブクラスからオーバーライドするのがより便利になります。また、これらのサブクラスに新しいメソッドを追加するというアイデアも必要です。Java でさえ、これを変更することはできません。その場合、Java コミュニティ全体がクラッシュします。サブクラスにメソッドを追加することを防ぐことはできません。クラスを拡張してメソッドをオーバーライドするのを止めることができるのは、あなただけです。

于 2013-06-17T12:32:52.167 に答える