4

次のように、スーパークラスといくつかのサブクラスがあります。

public abstract class A {
    public abstract int getValue();
}

public class B extends A {
    public int getValue() {
        return 1;
    }
}

public class C extends A {
    public int getValue() {
        return 123;
    }
}

public class D extends A {
    public int getValue() {
        return 15234;
    }
}

約 100 程度のサブクラスがあります。私にはマネージャーもいます:

public class Manager {
    public static ArrayList<A> list = new ArrayList<A>();
}

すべてのサブクラスのインスタンスを手動で作成してリストに追加せずに、 Atoのすべてのサブクラスのインスタンスを「魔法のように」追加するにはどうすればよいですか? listおそらく初期化ブロックを使用していますか?

編集

にアクセスlistする方法は重要ではありませんManager。静的になるように編集しました。

4

7 に答える 7

2

(2 回目の試行 - 最初の試行は、質問の誤解に基づいていました。)

あなたがやりたいことは、次のような(静的)リストを作成することだと思います。

  • 各サブクラスのインスタンスを 1 つだけ含み、
  • 事前に作成および設定されている
  • それ自体のインスタンスを作成/リストに追加する各サブクラスのコードは含まれません。

まず、インスタンス初期化ブロックはこれを行いません。インスタンスを作成すると、インスタンス初期化子が実行されます...そしてnew、これが発生するためにクラス(つまり、各サブクラス)に何かが必要です。

実行可能な唯一のアプローチは、毛むくじゃらのリフレクティブ コードを記述することだと思います。

  • クラスパス上のすべてのクラスを反復し、
  • を使用してそれぞれをロードしますClass.forName()
  • クラスが のサブクラスであるかどうかを反射的にテストしますA
  • そうであれば、クラスの引数なしコンストラクターを反射的に呼び出し、結果のインスタンスを「リスト」に追加します。

これは(IMO)かなりハッキーです!! そして、これらのサブクラスを検索する必要がある「パッケージスペース」を制限できない限り、費用がかかります。


実際、これ、特にサブクラスに異なるメソッドの実装を必要enumとする動作の違いがない場合は、 ... を使用してより適切に解決できる問題になる可能性があります。(たとえば、メソッドはコンストラクターを使用して初期化するプライベート変数を返すことができます。) @Paul Bellora の回答を参照してください。getValue()

(これが適用できないのは、一部のサブクラスの複数のインスタンスが必要な場合です。それは では不可能enumsです。)

于 2013-06-21T02:50:05.743 に答える
1

各クラスはコマンドを表します。

問題の説明に基づいてA列挙型のように聞こえます:

public enum A {

    B(1) {
        @Override
        public void execute() {
            //code and stuff
        }
    },
    C(123) {
        @Override
        public void execute() {
            //code and stuff
        }
    },
    D(15234) {
        @Override
        public void execute() {
            //code and stuff
        }
    };

    private final int value;

    private A(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public abstract void execute();
}

これで、各コマンドのインスタンスが 1 つだけになり、コマンドを簡単に繰り返すことができますA.values()

于 2013-06-21T03:04:02.037 に答える
0

クラスパススキャナーを使用して、ターゲットクラスを自動的に検出するのはどうですか:

   List<Class<?>> classes = CPScanner.scanClasses(new ClassFilter().packageName("com.foo.*").superClass(A.class));

ターゲット クラスを取得したので、newInstance メソッドを使用して簡単に初期化できます。

ところで、以下の maven 依存関係を使用して、指定されたスニペットを使用します。

<dependency>
    <groupId>net.sf.corn</groupId>
    <artifactId>corn-cps</artifactId>
    <version>1.1.1</version>
</dependency>

乾杯。

于 2013-06-24T15:43:21.130 に答える
0

1) クラス A の使用可能なすべてのサブクラスを見つける必要があります。そのためには、Java クラスパス上のすべてのクラスをスキャンする必要があります。簡単にするために、すべてのサブクラスが A.class と同じ場所にあると仮定できます。A は jar またはフォルダーにあるはずです。その実際の場所を次のように見つけることができます

URL url = A.class.getProtectionDomain().getCodeSource().getLocation();

2) それがフォルダーであると仮定しましょう (例: file:/D:/workspace1/x/target/classes/)。ここで、このフォルダーとサブフォルダー内のすべての .class ファイルを見ていく必要があります。そのために File.listFiles または Java 7 NIO2 を使用できます。2つのオプションがあります

a)各クラスをロードし、そのスーパークラスを確認します

   Class cls = Class.forName();
   if (cls.getSuperClass() == A.class) {
     ...

b) javaassist フレームワークhttp://www.javassist.orgなどを使用して、クラス ファイルを直接操作します。

DataInputStream ds = new DataInputStream(new BufferedInputStream(path));
ClassFile cf =  new ClassFile(ds);
String superClass = cf.getSuperClass();
if (superClass.equals("A")) {
    Class cls = Class.forName(cf.getName());
...

オプション b は実際に必要なクラスのみをロードします。オプション a はより単純ですが、フォルダ内のすべてのクラスをロードします

どちらの場合も、インスタンスを次のように作成します

A a = (A) cls.newInstance();

すべてのサブクラスに引数のないコンストラクターがあると仮定します

于 2013-06-21T03:31:11.350 に答える
0

これは少しハックな方法ですが、すべてのサブクラスが 1 つのフォルダー (実際のクラス ファイル) にある場合は、フォルダー内のファイルを反復処理してClassLoader. あなたのコードは、次のようなものになります-

for(String className : classNames){
    Class clazz = classLoader.loadClass(className);
    list.add(clazz.newInstance());
}

詳細については、ClassLoader APIを参照してください。また、これはあまり効率的ではないことに注意してください。ただし、これを 1 回実行するだけであれば問題ありません。

于 2013-06-21T02:45:10.627 に答える
0

あまり意味がありませんが... できる方法の 1 つは、Spring のコンポーネント スキャンと同様のことを行うことですPathMatchingResourcePatternResolver。それらを反復し、それが A のサブクラスである場合はリストに追加します。

于 2013-06-21T02:47:36.560 に答える
0

次のようになります。

public abstract class A {
    public A(Manager m) {
        m.list.add(this);
    }
    public abstract int getValue();
}

public class B extends A {
    public B(Manager m) {
        super(m);
    }
}

m.list.add(new A());このようにして、サブクラス化中に二度と対処する必要はありません。しかし、これがあなたが探しているものかどうかはわかりません...


編集 :

Manager でリストにアクセスする方法は重要ではありません。静的になるように編集しました。

シングルトンの使用を気にしない場合は、非常に基本的な実装を次に示します。

ただし、シングルトンの何が悪いのかを読んでください。

public class Manager {
   private static Manager instance = null;
   protected Manager() {
      // Exists only to defeat instantiation.
   }
   public static Manager getInstance() {
      if(instance == null) {
         instance = new Manager();
      }
      return instance;
   }
}

それで:

public abstract class A {
    public A() {
        Manager.getInstance().list.add(this);
    }
    public abstract int getValue();
}

public class B extends A {
}

しかし、これもデザインとしては満足のいくものではありません...

于 2013-06-21T02:52:02.153 に答える