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 Reflection有帮助吗?
答案 0 :(得分:17)
有效且干净地处理此问题的最佳方法是让foo返回对象的持有者类。
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();
}
答案 1 :(得分:3)
基本上你想要在执行时执行重载决策 - 你不能简单地做到这一点。
在某些案例中,visitor pattern可以提供帮助,但我认为不会在这里。我认为你坚持 你在这里得到的代码,或者反思。我从来没有像一些同事那样热衷于访客模式 - 总觉得有点乱 - 但是值得一想。
您是否可以让foo
调用正确的doSomething
重载,而不仅仅是返回值?这就是知道正在构造什么的代码 - 如果你可以通过适当的重载将一个对象传递给doSomething
,你最终会在一个地方找到特定于类型的逻辑。
在Java 7中,invokedynamic可能在这种情况下很有用 - 当然,C#4中的dynamic
类型会有所帮助 - 但我还没有足够好地调查invokedynamic。
答案 2 :(得分:3)
这里的问题可能是关注点分离。 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返回了什么样的东西。
答案 3 :(得分:2)
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.
答案 4 :(得分:0)
在Java中执行此操作没有任何意义。 Java是静态类型的,如果你要动态地编译它,你必须有一个switch语句才能调用不同对象上的不同方法。
示例 - 如果你有一个字符串或一个int并且想要“动态”地转换它(没有开关),你可以对不需要不同代码的两个操作做什么操作。
我想我是说如果你因为想要访问两个对象(不同方法)不同的东西而必须进行强制转换,那么如何在没有开关的情况下实际访问那个不同的方法?
一个例外可能是内在变量 - 对于那些你想要泛型的人来说,但是在类之外使用内部变量无论如何都是一个坏主意。
哦,你真正想要的是让所有的类都实现相同的接口 - 然后你就不用了。
施法应该非常罕见。