public Object foo(int opt){
if (opt == 0) return new String();
else if (opt == 1) return new Integer(1);
else if (opt == 2) return new Double(1);
else if ...
.. and many more
}
public void doSomething(String s){..}
public void doSomething(Integer i){..}
public void doSomething(Double d){..}
... and many more doSomething method
public static void main(String[] args){
...
Object o = foo(x); //x is a value obtained during runtime, e.g. from user input
//now I want to call doSomething method
// (1)
if (o instanceof String) doSomething((String) o);
else if (o instanceof Integer) doSomething((Integer) o);
else if (o instanceof Double) doSomething((Double) o);
...
// (2)
}
有没有更好的方法来简化(1)…(2)吗?
Java反射有帮助吗?
有效而简洁地处理这个问题的最好方法是让foo返回对象的一个holder类。
abstract class Holder<T> {
private final T object;
protected Holder(T object) { this.object = object; }
public T get() { return object; }
public abstract void doSomething();
}
public Holder foo(int opt) {
if (opt == 0) return new Holder<String>("") {
public void doSomething() { }
};
else if (opt == 1) return new Holder<Integer>(1) {
public void doSomething() { }
};
else if (opt == 2) return new Holder<Double>(1.0) {
public void doSomething() { }
};
// many more
}
public static void main(String... args) throws IOException {
Holder h = foo(x); //x is a value obtained during runtime, e.g. from user input
//now I want to call doSomething method
h.doSomething();
}
基本上你希望在执行时执行重载解析-你不可能非常简单地做到这一点。
在一些的情况下,访问者模式可以帮助,但我不认为它在这里。我认为你被困住了,或者你在这里得到的代码,或者反射。我从来没有像我的一些同事那样热衷于访客模式——它总是感觉有点乱——但这值得考虑。
你能让foo
调用正确的doSomething
过载而不是仅仅返回值吗?这段代码知道正在构造什么——如果你可以通过适当的重载传递一个对象来调用doSomething
,你最终会在一个地方得到特定类型的逻辑。
在Java 7中,invokedynamic可能会在这种情况下有用——当然c# 4中的dynamic
类型会有所帮助——但我还没有对invokedynamic进行足够的研究,不能肯定地说。
这里的问题可能是关注点分离问题之一。Java是一种面向对象的语言,它可能有助于尝试以面向对象的方式解决问题。在这种情况下,您可能会问为什么main应该关心Object o的类型。相反,您可以考虑使用一组类,每个类都知道如何以自己的方式做事。
abstract class Thing {
abstract void doSomething();
}
class IntegerThing extends Thing {
public void doSomething() { /*whatever*/ };
}
class FloatThing extends Thing {
public void doSomething() { /*whatever*/ };
}
//Then later:
int foo(int type) {
if(type == 0) return new IntegerThing(0);
if(type == 1) return new FloatThing(7.5);
if(type == 3) return new StringThing("Florence");
}
int main(String args[]) {
Thing something = foo(x);
something.doSomething();
}
你的foo()方法有效地变成了一个工厂,从那时起你不再需要关心foo返回了什么类型的东西
Java反射在一定程度上有所帮助,但是缺少了一部分数据。此外,反射通常会抛出许多需要捕获的已检查异常。(我在代码后面包含了一个列表)
保存"doSomething"方法的对象是什么?在本例中,我使用变量名"someObject"来表示包含"doSomething"方法的对象。你需要用更有意义的东西来代替它。
同样,只是一个警告,这将不会捕获派生类型,所以如果方法定义与给定的类型不匹配,您将得到一个方法未发现异常。
//now I want to call doSomething method
// (1)
Method method = someObject.getClass.getMethod("doSomething",new Class[] {o.getClass()});
method.invoke(someObject, new Object[] {o});
// (2)
警告:当以这种方式使用反射时,您需要处理以下异常:(顺便说一句,这不是一个不寻常的列表,反射通常在异常方面非常嘈杂)
NoSuchMethodException - if a matching method is not found or if the name is "<init>"or "<clinit>".
NullPointerException - if name is null
SecurityException - if access to the information is denied.
IllegalAccessException - if this Method object enforces Java language access control and the underlying method is inaccessible.
IllegalArgumentException - if the method is an instance method and the specified object argument is not an instance of the class or interface declaring the underlying method (or of a subclass or implementor thereof); if the number of actual and formal parameters differ; if an unwrapping conversion for primitive arguments fails; or if, after possible unwrapping, a parameter value cannot be converted to the corresponding formal parameter type by a method invocation conversion.
InvocationTargetException - if the underlying method throws an exception.
NullPointerException - if the specified object is null and the method is an instance method.
ExceptionInInitializerError - if the initialization provoked by this method fails.
在Java中这样做没有任何意义。Java是静态类型的,如果你要动态地强制转换它,你必须有一个switch语句来这样做,以便在不同的对象上调用不同的方法。
示例——如果你有一个字符串或整型,并且你想"动态"转换它(没有开关),你可以对它们做什么操作而不需要不同的代码?
我想我是说,如果你必须强制转换,因为你想要访问两个对象的不同之处(一个不同的方法),那么你如何在不切换的情况下访问那个不同的方法?
一个例外可能是内在变量——对于那些你想要泛型的人来说,但是在类之外使用内在变量是一个坏主意。
哦,你可能真正想要的是让所有的类实现相同的接口——这样你就不需要强制转换了。