3

我有一个超类型定义为:

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> {
    ...
}

这些子类型指定了未知参数....我有更多的 fe 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);

一切都会好起来的,但是这个模块的用户不想每次都在枚举中查找并手动转换。这可能会出错并花费大量时间进行调试:/....

我的问题是如何重构它或如何定义继承结构以允许用户动态转换?是否可以?

编辑: 我正在解析来自网络的数据包。供应商类型标识符和值/掩码类型有很多不同的类型 - 这些字段对于每个这种组合都是常量,所以我将其定义为枚举常量。Fe 20 只有 TypeID 不同,但 VendorID 相同,都可以表示为 Integer,接下来的 10 个 VendorID 和 TypeID 不同,但都可以表示为 Short 等等。

4

2 回答 2

2

目前还不清楚为什么你应该完全施放。一旦SOME_TYPE写入您的源代码setsetValue方法的类型是硬编码的(在您的示例中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();
}

关键部分是:不要使用 an enum,因为 anenum不能有类型参数。interface您可以像下一个片段一样使用纯文本。接口中的每个引用都指向一个工厂。每个工厂都知道 a) 抽象类型和 b) 具体类型。为了使泛型满意,您必须将 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();然后转到行首,点击Ctrl2+ L(“分配给本地变量”),Eclipse 会AType<String> instantiate =为您替换该部分。


1如果您的用户不知道该setValue方法的正确类型,那么您就问错了问题。在这种情况下,您应该问诸如“如何设计通用安全转换设施?”之类的问题。

于 2012-08-01T16:39:07.127 回答
0

也许使用这样的 setValue 方法:

public void setValue(Object value) {
    if (value == null)
        throw new IllegalArgumentException("Value is mandatory.");
    this.value = (T)value;
}

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

希望这可以帮助

于 2012-08-01T11:41:32.380 回答