我有一个超类定义为:
public abstract class AType<T> {
....
private T value;
private T mask;
public T getValue() {
if (isMasking())
return null;
return this.value;
}
public void setValue(T value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = value;
}
protected T getMask() {
if (!isMasking())
return null;
return this.mask;
}
protected void setMask(T mask) {
if (mask == null)
throw new IllegalArgumentException("Mask is mandatory.");
this.setMasking(true);
this.mask = mask;
}
...
}
和少数几种类型:
public class SpecType extends AType<Integer> {
...
}
这些子类型指定了未知参数....我有更多的f.e. IPv4,Long等
现在我需要在运行时以某种方式进行动态转换... 我在枚举中定义了这些类:
public enum Type {
SOME_TYPE(new TypeID(0, (short) 0), OFMU16.class,
new Instantiable<AType<?>>() {
@Override
public SpecType instantiate() {
return new SpecType(new OFMatchTypeIdentifier(0, (short) 0));
}
}),...;
...
public Class<? extends AType<?>> toClass() {
return this.clazz;
}
...
}
我想做类似的事情:
AType<?> type = SOME_TYPE.newInstance(); //this works
SOME_TYPE.toClass().cast(type).setValue(10); //this don't work
所以我必须静态地做:
((SpecType) type).setValue(10);
一切都会好的,但是这个模块的用户不希望每次都查看枚举和手动。这可能会犯错误并花费大量时间进行调试:/ ....
我的问题是如何重构这个或如何定义继承结构以允许用户动态转换?有可能吗?
修改 我正在解析来自网络的数据包。供应商类型标识符和值/掩码类型有很多不同的类型 - 这些字段对于每个组合都是常量,因此我将其定义为枚举常量。 F.E. 20具有不同的TypeID但具有相同的VendorID,并且所有这些都可以表示为Integer,接下来的10在VendorID和TypeID上有所不同,但所有这些都可以表示为Short等等。
答案 0 :(得分:2)
目前还不清楚你为什么要施展。只要将SOME_TYPE
写入您的源代码或,就会对setValue
方法的类型进行硬编码(在您的示例int
或Integer
中)不需要运行时检查 - 您需要编译时检查。
因此,我认为以下代码段是您的API用户应该如何编码:
public class TypeTest {
public static void main(String[] args) {
AType<Integer> type0 = Types.SOME_TYPE_0.instantiate();
type0.setValue(10);
AType<String> type1 = Types.SOME_TYPE_1.instantiate();
type1.setValue("foo");
}
}
我已将您的示例简化为理解泛型部分所需的最低限度:
abstract class AType<T> {
private T value;
// standard getter/setter
public T getValue() { return this.value; }
public void setValue(T value) { this.value = value; }
}
class SpecTypeInt extends AType<Integer> {
}
class SpecTypeString extends AType<String> {
}
interface Instantiable<T> {
T instantiate();
}
关键部分是:不要使用enum
,因为enum
不能有类型参数。您可以使用普通interface
代替下一个代码段。界面中的每个引用都指向工厂。每个工厂都知道a)抽象类型和b)具体类型。为了使Generics满意,你必须将a)和b)与? extends X
粘合在一起。
interface Types {
Instantiable<? extends AType<Integer>> SOME_TYPE_0 = new Instantiable<SpecTypeInt>() {
@Override
public SpecTypeInt instantiate() {
return new SpecTypeInt();
}
};
Instantiable<? extends AType<String>> SOME_TYPE_1 = new Instantiable<SpecTypeString>() {
@Override
public SpecTypeString instantiate() {
return new SpecTypeString();
}
} ;
}
清理:您的用户必须查看界面:是的,他必须在任何情况下,因为他必须知道setValue
1 。没有解决方案可以绕过这个。尽管Eclipse可以帮助您和您的用户:main
只需键入Types.SOME_TYPE_1.instantiate();
然后转到行的开头,按 Ctrl 2 + L (“分配给loccal变量”)和Eclipse替换AType<String> instantiate =
部分。
<小时/> 1 如果您的用户不知道
setValue
方法的正确类型,那么您提出的问题是错误的。在这种情况下,您应该问过“如何设计通用安全转换工具?”。
答案 1 :(得分:0)
也许使用这样的setValue方法:
public void setValue(Object value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = (T)value;
}
虽然你有一个未经检查的演员。
希望这有帮助