[英]Instantiate different versions of same class with changed constructor and "abstract" modifier
I am writing a Java program which uses external libraries.我正在编写一个使用外部库的 Java 程序。 I want to write my code so it is compatible with two different versions of one specific library.
我想编写我的代码,以便它与一个特定库的两个不同版本兼容。
The problem is, they changed a class (called Configuration
in my example) to abstract, added an abstract method and changed the constructor from a no-argument to parameterized constructor.问题是,他们将 class(在我的示例中称为
Configuration
)更改为抽象,添加了一个抽象方法并将构造函数从无参数更改为参数化构造函数。 I am not able to influence the external library and I have to work with what I got.我无法影响外部图书馆,我必须使用我得到的东西。
Old class example旧 class 示例
public class Configuration {
public Configuration() {
//Some code...
}
public static void main(String[] args) {
Configuration conf = new Configuration();
//Some code...
}
}
New class example新 class 示例
public abstract class Configuration {
public Configuration(boolean isCool, Map<String, String> entries, Object otherThing) {
//Some code...
}
public abstract int doSomething();
public static void main(String[] args) {
Configuration conf = new MyConfiguration(false, null, new Object());
//Some code...
}
}
My implementation我的实现
public class MyConfiguration extends Configuration {
public MyConfiguration(boolean isCool, Map<String, String> entries, Object otherThing) {
super(isCool, entries, otherThing);
}
public MyConfiguration() {
//needs super constructor call!?
}
public int doSomething() {
//Some code...
return -1;
}
public static void main(String[] args) {
int version = 0;
Configuration conf = version > 2 ? new MyConfiguration(false, null, new Object()) : new MyConfiguration();
//Some code...
}
}
The no-argument constructor of my custom class MyConfiguration
needs to call the constructor of the superclass in order to be compiled.我自定义的 class
MyConfiguration
的无参构造函数需要调用超类的构造函数才能编译。 However, the old class does not have a parameterized constructor, so I will run into an error if I do that.但是,旧的 class 没有参数化构造函数,所以如果我这样做会遇到错误。
Your example is a bit odd, as the new class example contains a reference to your MyConfiguration
.您的示例有点奇怪,因为新的 class 示例包含对您的
MyConfiguration
的引用。
Generally, you're better off maintaining two versions of your MyConfiguration
class or dropping support for the old version of that library.通常,您最好维护两个版本的
MyConfiguration
class 或放弃对该库旧版本的支持。 Or, consider not to use a library that makes such drastic changes.或者,考虑不要使用做出如此剧烈变化的库。
For completeness, there is a hack addressing your literal question.为了完整起见,有一个技巧可以解决您的字面问题。 Assuming that the class
Configuration
is not serializable as shown or at least, the old version isn't, you can utilize a specific Serialization behavior.假设 class
Configuration
不可序列化,或者至少旧版本不是,您可以利用特定的序列化行为。 When a serializable class extends a non-serializable class, deserializing an instance of it will cause the invocation of the superclass's default constructor.当可序列化的 class 扩展不可序列化的 class 时,反序列化它的实例将导致调用超类的默认构造函数。
So, the code utilizing it could look like:因此,使用它的代码可能如下所示:
public class MyConfiguration extends Configuration implements Serializable {
public MyConfiguration(
boolean isCool, Map<String, String> entries, Object otherThing) {
super(isCool, entries, otherThing);
// new version setup
}
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException {
// old version, Configuration's default constructor has been called
}
public static MyConfiguration create() {
return new ByteArrayOutputStream() {
MyConfiguration get() {
final short numFields = 0;
try {
Configuration.class.getConstructor(); // check for old version
DataOutputStream os = new DataOutputStream(this);
// using import static java.io.ObjectStreamConstants.*;
os.writeShort(STREAM_MAGIC);
os.writeShort(STREAM_VERSION);
os.write(TC_OBJECT);
os.write(TC_CLASSDESC);
os.writeUTF(MyConfiguration.class.getName());
os.writeLong(ObjectStreamClass.lookup(MyConfiguration.class)
.getSerialVersionUID());
os.write(SC_SERIALIZABLE);
os.writeShort(numFields);
os.write(TC_ENDBLOCKDATA);
os.write(TC_NULL);
os.flush();
return (MyConfiguration)new ObjectInputStream(
new ByteArrayInputStream(buf, 0, count)).readObject();
}
catch(NoSuchMethodException ex) {
// new version
return new MyConfiguration(false, Map.of(), null);
}
catch(IOException|ClassNotFoundException ex) {
throw new AssertionError(ex);
}
}
}.get();
}
public int doSomething() {
//Some code...
return -1;
}
}
This class can be compiled against the new version.这个 class 可以针对新版本进行编译。 The
create()
method first checks for the presence of the default constructor. create()
方法首先检查是否存在默认构造函数。 When absent, ie running with the new version, the new constructor is called.如果不存在,即使用新版本运行,则调用新的构造函数。 Otherwise, it deserializes an instance with a stream having no data, so after the superclass's default constructor has been called, the
readObject
may perform the actual setup.否则,它使用没有数据的 stream 反序列化一个实例,因此在调用超类的默认构造函数之后,
readObject
可能会执行实际设置。
This all requires a lazy verifier that doesn't care for the incompatible constructors, as long as no-one tries to actually invoke the constructor.这一切都需要一个惰性验证器,它不关心不兼容的构造函数,只要没有人尝试实际调用构造函数。 As said at the beginning, you're likely better off with other solutions.
如开头所说,您可能会使用其他解决方案更好。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.