以下代码应该可以解决您的问题。该Main
课程模拟您的主要课程。类A
模拟您要扩展的基类(并且您无法控制)。ClassB
是 class 的派生类A
。接口C
模拟 Java 没有的“函数指针”功能。我们先来看代码...
以下是 class A
,您要扩展但无法控制的类:
/* 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);
}
}
下面是 class B
,虚拟派生类。请注意,由于它 extends A
,它必须 importpackageA.A
并且 classA
必须在 class 的编译时可用B
。带有参数 C 的构造函数是必不可少的,但实现接口C
是可选的。如果B
implements 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
or的引用packageB.B
。如果有,类加载器将在packageA.A
尝试加载其中一个时在没有的平台上抛出异常。
它是如何工作的?
首先Class.forName()
,它检查A
平台上是否有可用的类。如果是,请让类加载器加载 class B
,并将生成的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
没有“无参数构造函数”怎么办?
只需将所需的参数添加到类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
没有对它的任何引用)。在第二次运行中,该类被显式编译,因此您得到了预期的结果。
为了帮助您解决我的问题,这里是设置 Nimbus 外观的正确方法的链接:
Nimbus 外观和感觉