我正在重构一些遗留代码并遇到了一个问题,我确信它有一个优雅的解决方案 - 但我无法完全实现。
最初有一大堆类扩展了抽象类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
中的那个。
有人能指出我正确的方向吗?
答案 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。 这是一种鸡肉和鸡蛋问题! :)