Java:如何动态重写类的方法(类最终不在类路径中)?
如何动态+有条件地调用类的方法?Java:如何动态重写类的方法(类最终不在类路径中)?,java,reflection,classpath,openjdk,nimbus,Java,Reflection,Classpath,Openjdk,Nimbus,如何动态+有条件地调用类的方法? (类最终在类路径中是而不是) 比方说,我需要类NimbusLookAndFeel,但在某些系统上它不可用(即OpenJDK-6) 因此,我必须能够: 了解类是否可用(在运行时) 如果不是这样,跳过整个过程 如何覆盖动态加载类的方法 (从而创建它的匿名内部子类) 代码示例 public static void setNimbusUI(最终i方法) 抛出不受支持的LookandFeelException{ //NimbusLookAndFeel现在可能可用 UI
(类最终在类路径中是而不是) 比方说,我需要类
NimbusLookAndFeel
,但在某些系统上它不可用(即OpenJDK-6
)
因此,我必须能够:
- 了解类是否可用(在运行时)
- 如果不是这样,跳过整个过程
- 如何覆盖动态加载类的方法 (从而创建它的匿名内部子类)
public static void setNimbusUI(最终i方法)
抛出不受支持的LookandFeelException{
//NimbusLookAndFeel现在可能可用
UIManager.setLookAndFeel(新的NimbusLookAndFeel(){
@凌驾
公共UIDefaults getDefaults(){
UIDefaults ret=super.getDefaults();
方法:执行(ret);
返回ret;
}
});
}
编辑:现在,我按照建议编辑了代码,使用try-catch拦截
NoClassDefFoundError
。它失败了。我不知道,是不是OpenJDK的错。我得到了InvocationTargetException
,这是由NoClassDefFoundError
引起的。有趣的是,我无法捕获InvocationTargetException
:它还是被抛出的
EDIT2::发现原因:我在测试方法周围包装
SwingUtilities.invokeAndWait(…)
,当加载Nimbus失败时,调用invokeAndWait
抛出NoClassDefFoundError
EDIT3::有人能澄清一下哪里会发生NoClassDefFoundError吗?因为似乎总是调用方法,而不是使用不存在的类的实际方法。您可以使用类来实现这一点 即:
如果在当前类路径上找不到,上面的句子将抛出ClassNotFoundException。如果未引发异常,则可以使用
c
中的newInstance()
方法创建your.package.YourClass类的对象。如果需要调用特定的构造函数,可以使用getConstructors
方法获取一个构造函数,并使用它创建一个新实例。使用BCEL动态生成动态子类
呃,您不能将要扩展的类放入编译时类路径中,像往常一样编写子类,并在运行时显式触发加载子类,并处理链接器引发的任何异常以指示缺少超类吗?了解该类是否可用(在运行时)
将用法放入try块中 如果不是这样,请跳过整件事
... 并将捕捉块留空(代码气味?!) 如何覆盖动态加载类的方法
只要这样做,并确保编译时依赖关系得到满足。你把事情搞混了。重写发生在编译时,而类加载是运行时的事情 为了完整性,您编写的每个类都会在需要时由运行时环境动态加载 因此,您的代码可能如下所示:
public static void setNimbusUI(final IMethod<UIDefaults> method)
throws UnsupportedLookAndFeelException {
try {
// NimbusLookAndFeel may be now available
UIManager.setLookAndFeel(new NimbusLookAndFeel() {
@Override
public UIDefaults getDefaults() {
final UIDefaults defaults = super.getDefaults();
method.perform(defaults);
return defaults;
}
});
} catch (NoClassDefFoundError e) {
throw new UnsupportedLookAndFeelException(e);
}
}
public static void setNimbusUI(最终i方法)
抛出不受支持的LookandFeelException{
试一试{
//NimbusLookAndFeel现在可能可用
UIManager.setLookAndFeel(新的NimbusLookAndFeel(){
@凌驾
公共UIDefaults getDefaults(){
最终UIDefaults=super.getDefaults();
方法。执行(默认值);
返回默认值;
}
});
}捕获(NoClassDefFounde错误){
抛出新的不受支持的LookandFeelException(e);
}
}
以下代码应该可以解决您的问题。Main
类模拟您的主类。类A
模拟要扩展的基类(并且您无法控制)。ClassB
是ClassA
的派生类。接口C
模拟Java没有的“函数指针”功能。让我们先看看代码
下面是classA
,您想要扩展的类,但无法控制:
/* 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);
}
}
下面是classB
,虚拟派生类。请注意,由于它扩展了A
,因此必须导入packageA.A
,并且类A
必须在类B
的编译时可用。带有参数C的构造函数是必需的,但实现接口C
是可选的。如果B
实现了C
,则可以方便地直接(无反射)调用B
实例上的方法。在B.doSomething()
中,调用super.doSomething()
是可选的,这取决于您是否需要,但调用c.doSomething()
是必需的(如下所述):
下面是棘手的界面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()
中,它检查平台上是否有ClassA
。如果是,请让类加载器加载类B
,并将生成的class
对象存储在classB
中。否则,ClassNotFou
/* 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);
}
}
/* 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");
}
}
}
@Override
public void doSomething(String s) {
super.doSomething1(s);
c.doSomethingAfter1(s);
super.doSomething2(s);
c.doSomethingAfter2(s);
}
$ mkdir classes
$
$
$
$ javac -cp src -d classes src/Main.java
$ java -cp classes Main
packageA.A not found. Go without it!
$
$
$
$ javac -cp src -d classes src/packageB/B.java
$ java -cp classes Main
This is from packageA.A: World
This is from anonymous inner class: Hello