在Java中使用枚举时如何避免过多的代码重复

时间:2011-07-15 09:30:10

标签: java inheritance override superclass

我正在重构一些遗留代码并遇到了一个问题,我确信它有一个优雅的解决方案 - 但我无法完全实现。

最初有一大堆类扩展了抽象类BaseType。这些类中的每一个都有一个枚举 - XmlElementTag - 具有特定于类的值:

enum XmlElementTag {value1, value2, value3}

他们每个人都有一个方法:

private XmlElementTag getTag(String s){
    XmlElementTag ret = null;
    try {
        ret = XmlElementTag.valueOf(s);
    } catch (Exception e) {
        Log.e(this, s+" is not supported tag");
    }
    return ret;
}

每个类都有这个完全相同的getTag方法,但显然它们都指的是特定于它们所在类的XmlElementTag枚举。所以,我想摆脱这个代码如果我可以重复。

我认为也许我可以使用标记接口来解决这个问题,因此创建了一个每个XmlElementTag枚举现在继承并重写getTag方法并将其放在超类中的方法。 / p>

所以我在每个班级都有这个:

private XmlElementTag implements GenericTag {value1, value2, value3}; 

这是BaseType超类:

public interface GenericTag {}

protected GenericTag getTag(String tagName){
    XmlElementTag tag = null;
    try {
        tag = XmlElementTag.valueOf(tagName);
    } catch (Exception e) {
        Log.e(this, tagName+" is not supported tag");
    }
    return tag;
}

但是再次这不起作用,因为BaseType超类不知道XmlElementTag是什么; Java不允许抽象类变量;并且在BaseType中创建此元素将不起作用,因为getTag代码将始终引用此枚举,而不是类BaseType中的那个。

有人能指出我正确的方向吗?

5 个答案:

答案 0 :(得分:3)

您可以将XmlElementTag个元素合并为一个enum,并为每个派生类型建立EnumSet apropos。有一个例子here

附录:在此方案中,getTag()将成为合并enum的单一方法。每个派生类都会使用它认为有效的getTag()来调用Set。该方法可能具有如下签名:

public static XmlElementTag getTag(Set valid, String s) { ... }

答案 1 :(得分:3)

我想你可以编写一个静态通用辅助方法来完成getTag所做的事情。它需要在引擎盖下使用反射,并且很可能要求您将枚举的Class对象作为参数传递。

但IMO,你不应该。 getTag()方法有点不对劲。它将有效的错误输入转换为null。从两个方面来看,这是错误的:

  • 在大多数情况下,“你给我的坏东西”不应该被视为“你什么也没给我”。
  • 如果您不小心谨慎,那些null值将会以NullPointerException s来回过头来咬你。

实际上,您的应用程序代码应该捕获并处理转换出错时出现的IllegalArgumentException,或者它应该允许异常冒泡到可以报告为的顶部(例如)解析输入流时出错。

(我不认为enum可以扩展或扩展,所以我不认为你的enum可以继承这个类的通用版本。)

答案 2 :(得分:1)

您可以使用generics

基地是

public abstract class Base {
protected static <T extends Enum<T>> T getTag(Class<T> enumType, String s) {
    T ret = null;
    try {
        ret = Enum.valueOf(enumType, s);
    } catch (Exception e) {
        System.err.println(s + " is not supported tag");
    }
    return ret;
}

protected abstract <T extends Enum<T>> T getTag(String s);
}

您的众多课程的getTag()更短(所有逻辑都在Base

public class ClassA extends Base {
enum XmlElementTag {
    UL, LI
}

@Override
protected XmlElementTag getTag(String s) {
    return Base.getTag(XmlElementTag.class, s);
}
}

ClassB同样的事情)

答案 3 :(得分:1)

不幸的是,Java枚举没有一个好的元类(Class是邪恶的)。但是,这里你真正需要的是枚举值的列表(数组)。

因为它是一种私有方法,所以你也可以使用组合。

import static java.util.Objects.requireNonNull;

/* pp */ class EnumFinder<E extends Enum<E>> {
    private final E[] tags;
    protected BaseType(E[] tags) {
        this.tags = requireNonNull(tags);
    }

    public E getTag(String name) {
        requireNonNull(name);
        for (E tag : tags) {
            if (name.equals(tag.name())) {
                return tag;
            }
        }
        Log.e(this, name+" is not supported tag"); // (sic)
        return null; // (sic)
    }
    ...
}

public class DerivedType {
    private static final EnumFinder<XmlElementType> finder = // note, shared
        new EnumFinder<>(XmlElementType.values());
    ...
        finder.getTag(name)
    ...
}

(如果你真的想要创建一个Map<String,E>。对于合理大小的枚举来说是不必要的。)

如果你真的想使用继承,那就大致相同了。 (不幸的是,因为我们正在使用数组,除非你在代码中添加更多的样板,否则这个代码会为每个实例创建一个不必要的额外数组 - 可能不是一个重要问题,但可能是。):

/* pp */ abstract class BaseType<E extends Enum<E>> {
    private final E[] tags;
    protected BaseType(E[] tags) {
        this.tags = requireNonNull(tags);
    }

    public E getTag(String name) {
        requireNonNull(name);
        for (E tag : tags) {
            if (name.equals(tag.name())) {
                return tag;
            }
        }
        Log.e(this, name+" is not supported tag"); // (sic)
        return null; // (sic)
    }
    ...
}
public class DerivedType extends BaseType<XmlElementType> {
    public DerivedType() {
        super(XmlElementType.values());
    }
    ...
        this.getTag(name)
    ...
}

答案 4 :(得分:0)

我认为你想要达到这样的目标(如果我错了,请纠正我):

interface GenericTag {

    public GenericTag fromString(String str) throws IllegalArgumentException;
}


class BaseType {

    protected GenericTag getTag(String tagName) {
        GenericTag tag = null;
        try {
            tag = tag.fromString(tagName); 
        } catch (Exception e) {
            Log.e(this, tagName+" tag is not supported");
        }
        return tag;
    }

}

class ConcreteTypeA extends BaseType {

    enum XmlElementTag implements GenericTag {
        TAG1, TAG2;

        public GenericTag fromString(String str) throws IllegalArgumentException {
            return XmlElementTag.valueOf(str);
        }
    }
}

然而,这将永远不会奏效。你需要fromString方法来返回实现GenericTag的适当类的实例(在本例中为enum),但是fromString方法必须由那个你还没有的具体类来执行,所以你会得到一个NullPointerException。 这是一种鸡肉和鸡蛋问题! :)