使Java中的泛型变得复杂

时间:2012-08-01 09:53:12

标签: java generics

我有一个超类定义为:

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等等。

2 个答案:

答案 0 :(得分:2)

目前还不清楚你为什么要施展。只要将SOME_TYPE写入您的源代码,就会对setValue方法的类型进行硬编码(在您的示例intInteger中)不需要运行时检查 - 您需要编译时检查。

因此,我认为以下代码段是您的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;
}

虽然你有一个未经检查的演员。

希望这有帮助