Java 中向后兼容的包装类
这里有一篇关于维护 Java 向后兼容性的有趣的文章。在包装类部分,我实际上无法理解包装类完成的任务。在 MyApp
的以下代码中,WrapNewClass.checkAvailable()
可以替换为 Class.forName("NewClass")
。
static {
try {
WrapNewClass.checkAvailable();
mNewClassAvailable = true;
} catch (Throwable ex) {
mNewClassAvailable = false;
}
}
考虑 NewClass
何时不可用。在我们使用包装器的代码中(见下文),我们所做的就是用存在的类替换一个不存在的类,但该类无法编译,因为它使用了一个不存在的类。
public void diddle() {
if (mNewClassAvailable) {
WrapNewClass.setGlobalDiv(4);
WrapNewClass wnc = new WrapNewClass(40);
System.out.println("newer API is available - " + wnc.doStuff(10));
}else {
System.out.println("newer API not available");
}
}
谁能解释为什么这会有所不同?我认为这与 Java 编译代码的方式有关 - 我对此不太了解。
There is an interesting article here on maintaing backwards compatibility for Java. In the wrapper class section, I can't actually understand what the wrapper class accomplishes. In the following code from MyApp
, WrapNewClass.checkAvailable()
could be replaced by Class.forName("NewClass")
.
static {
try {
WrapNewClass.checkAvailable();
mNewClassAvailable = true;
} catch (Throwable ex) {
mNewClassAvailable = false;
}
}
Consider when NewClass
is unavailable. In the code where we use the wrapper (see below), all we have done is replace a class that doesn't exist, with one that exists, but which can't be compiled as it uses a class that doesn't exist.
public void diddle() {
if (mNewClassAvailable) {
WrapNewClass.setGlobalDiv(4);
WrapNewClass wnc = new WrapNewClass(40);
System.out.println("newer API is available - " + wnc.doStuff(10));
}else {
System.out.println("newer API not available");
}
}
Can anyone explain why this makes a difference? I assume it has something to do with how Java compiles code - which I don't know much about.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我在 spring 和 richfaces 中看到过这种行为。例如,Spring 执行以下
private static
内部类,它在其中引用 JSF 类Class.forName(..)
Class.forName(..) 一个 JSF 类注意内部类在引用它们之前不会加载它们,因此存在不满足的依赖关系是可以的。
(spring 类是 org.springframework.web.context.request.RequestContextHolder )
I've seen this behaviour in spring and richfaces. Spring, for example, does the following
private static
inner class where it references the JSF classesClass.forName(..)
a JSF classNote that inner classes are not loaded until they are referenced, so it is OK to have a dependency that is not met in it.
(The spring class is
org.springframework.web.context.request.RequestContextHolder
)这样做的要点是拥有针对某些在运行时可能不可用的类编译的代码。 WrapNewClass必须存在于javac的类路径中,否则这个东西无法编译。但是,它可能不存在于运行时的类路径中。
如果 mNewClassAvailable 为 false,您引用的代码将避免引用 WrapNewClass。因此,它只会打印“新 API 不可用”消息。
然而,我不能说我印象深刻。一般来说,我见过这种用 java.lang.reflect 安排的事情,而不是尝试捕获异常。顺便说一句,即使编译后,类也不会出现在视野中。
The point of this is to have code which is compiled against some class which may not be available at runtime. WrapNewClass has to be present in the classpath of javac, or this thing can't be compiled. However, it can be absent from the classpath at runtime.
The code you quote avoids references to WrapNewClass if mNewClassAvailable is false. Thus, it will just print the 'new API not available' message.
However, I can't say that I'm impressed. In general, I've seen this sort of thing arranged with java.lang.reflect instead of trying to catch the exception. That, in passing, allows the class to be nowhere in sight even when compiled.
长期以来,我一直需要在 JSE 中支持自 1.1 版以来的所有 JVM,并使用这些包装技术来兼容地支持可选 API——也就是说,API 可以使应用程序更好地工作,但对于应用程序来说并不是必需的。
我使用的两种技术似乎在您引用的文章中进行了描述(很差?)。我不会对此进行进一步评论,而是提供我如何做到这一点的真实示例。
最简单 - 静态包装方法
需要:调用 API(如果可用),否则不执行任何操作。这可以针对任何 JVM 版本进行编译。
首先,设置一个具有反射方法的静态
Method
,如下所示:并包装反射方法,而不是使用直接调用:
更难 - 静态包装类
需要:调用 API(如果可用) ,或以其他方式调用较旧的 API 以获得等效但降级的功能。这必须针对较新的 JVM 版本进行编译。
首先设置一个静态包装类;这可能是一个静态单例包装器,或者您可能需要包装每个实例创建。下面的示例使用静态单例:
并创建一个子类以在可用时提供更新的功能:
I have long had the need to support every JVM since 1.1 in JSE and have used these kind of wrapping techniques to compatibly support optional APIs - that is, APIs which make the application work better, but are not essential to it.
The two techniques I use seem to be (poorly?) described in the article you referenced. Rather than comment further on that, I will instead provide real examples of how I have done this.
Easiest - Static Wrapper Method
Need: To invoke an API if it is available, or otherwise do nothing. This can be compiled against any JVM version.
First, set up a static
Method
which has the reflected method, like so:and wrap the reflected method instead of using a direct call:
Harder - Static Wrapper Class
Need: To invoke an API if it is available, or otherwise invoke an older API for equivalent, but degraded, functionality. This must be compiled against the newer JVM version.
First set up a static wrapper class; this may be a static singleton wrapper, or you might need to wrap every instance creation. The example which follows uses a static singleton:
and create a subclass to provide the newer functionality when available: