次のコードで問題が解決するはずです。Main
クラスは、メイン クラスをシミュレートします。ClassA
は、拡張する基本クラスをシミュレートします (そして、制御できません)。ClassB
は class の派生クラスですA
。インターフェイスC
は、Java にはない「関数ポインタ」機能をシミュレートします。最初にコードを見てみましょう...
以下はA
、拡張したいが制御できないクラスである class です。
/* src/packageA/A.java */
package packageA;
public class A {
public A() {
}
public void doSomething(String s) {
System.out.println("This is from packageA.A: " + s);
}
}
以下はB
、ダミーの派生クラスである class です。を拡張しているため、A
インポートする必要がありpackageA.A
、 classA
のコンパイル時に class を使用できる必要があることに注意してくださいB
。パラメーター C を持つコンストラクターは必須ですが、インターフェイスの実装C
はオプションです。B
が を実装している場合は、 のインスタンスで直接 (リフレクションなしC
で) メソッドを呼び出すことができます。B
ではB.doSomething()
、呼び出しsuper.doSomething()
はオプションであり、必要かどうかによって異なりますが、呼び出しc.doSomething()
は必須です (以下で説明します)。
/* src/packageB/B.java */
package packageB;
import packageA.A;
import packageC.C;
public class B extends A implements C {
private C c;
public B(C c) {
super();
this.c = c;
}
@Override
public void doSomething(String s) {
super.doSomething(s);
c.doSomething(s);
}
}
以下はトリッキーなインターフェースC
です。オーバーライドするすべてのメソッドをこのインターフェイスに入れるだけです。
/* src/packageC/C.java */
package packageC;
public interface C {
public void doSomething(String s);
}
主なクラスは次のとおりです。
/* src/Main.java */
import packageC.C;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) {
doSomethingWithB("Hello");
}
public static void doSomethingWithB(final String t) {
Class classB = null;
try {
Class classA = Class.forName("packageA.A");
classB = Class.forName("packageB.B");
} catch (ClassNotFoundException e) {
System.out.println("packageA.A not found. Go without it!");
}
Constructor constructorB = null;
if (classB != null) {
try {
constructorB = classB.getConstructor(C.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
C objectB = null;
if (constructorB != null) {
try {
objectB = (C) constructorB.newInstance(new C() {
public void doSomething(String s) {
System.out.println("This is from anonymous inner class: " + t);
}
});
} catch (ClassCastException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
if (objectB != null) {
objectB.doSomething("World");
}
}
}
なぜコンパイルして実行するのですか? クラスでは、 のみがインポートされ、またはへの参照がない
ことがわかります。存在する場合、クラスローダーは、それらのいずれかをロードしようとすると、存在しないプラットフォームで例外をスローします。Main
packageC.C
packageA.A
packageB.B
packageA.A
それはどのように機能しますか?
最初のClass.forName()
では、クラスA
がプラットフォームで使用可能かどうかを確認します。そうである場合は、クラスローダに class をロードするように依頼し、結果のオブジェクトを にB
格納します。それ以外の場合は によってスローされ、プログラムは class なしで実行されます。Class
classB
ClassNotFoundException
Class.forName()
A
次に、classB
null でない場合はB
、単一のC
オブジェクトをパラメーターとして受け入れるクラスのコンストラクターを取得します。Constructor
オブジェクトを に格納しますconstructorB
。
次に、constructorB
null でない場合は、呼び出してオブジェクトconstructorB.newInstance()
を作成しB
ます。パラメーターとしてオブジェクトがあるC
ため、インターフェイスを実装する匿名クラスを作成しC
、インスタンスをパラメーター値として渡すことができます。これは、匿名を作成するときに行うことと同じですMouseListener
。
(実際には、上記のブロックを分離する必要はありませんtry
。これは、私が何をしているのかを明確にするために行っています。)
B
implementsを作成した場合は、この時点でオブジェクトを参照としてC
キャストし、オーバーライドされたメソッドを (リフレクションなしで) 直接呼び出すことができます。B
C
クラスA
に「パラメーターなしのコンストラクター」がない場合はどうなりますか? 必要なパラメータを class 、 like
に追加し、 の代わりにcallするだけです。を作成するときに、 などの追加のパラメータも追加します。B
public B(int extraParam, C c)
super(extraParam)
super()
constructorB
classB.getConstructor(Integer.TYPE, C.class)
Strings
と Stringはどうなりt
ますか?
t
匿名クラスによって直接使用されます。がobjectB.doSomething("World");
呼び出されると、"World"
がs
class に提供されB
ます。super
は (明らかな理由で) 匿名クラスでは使用できないため、使用するすべてのコードはsuper
class に配置されB
ます。
super
複数回参照したい場合は?
次のようにテンプレートを書くだけB.doSomething()
です:
@Override
public void doSomething(String s) {
super.doSomething1(s);
c.doSomethingAfter1(s);
super.doSomething2(s);
c.doSomethingAfter2(s);
}
もちろん、インターフェースを変更して とC
を含める必要がdoSomethingAfter1()
ありdoSomethingAfter2()
ます。
コードをコンパイルして実行する方法は?
$ mkdir クラス
$
$
$
$ javac -cp src -d クラス src/Main.java
$ java -cp クラス メイン
packageA.A が見つかりません。それなしで行く!
$
$
$
$ javac -cp src -d クラス src/packageB/B.java
$ java -cp クラス メイン
これは、packageA.A: World からのものです。
これは匿名の内部クラスからのものです: こんにちは
最初の実行では、クラスpackageB.B
はコンパイルされMain.java
ません (クラスへの参照がないため)。2 回目の実行では、クラスが明示的にコンパイルされるため、期待どおりの結果が得られます。
あなたの問題に私の解決策を当てはめるのを助けるために、Nimbus のルック アンド フィールを設定する正しい方法へのリンクを次に示します。
Nimbus のルック アンド フィール