如何“动态”将Object类型的实例强制转换为其特定的数据类型?

时间:2011-04-11 05:19:20

标签: java reflection types casting dynamic

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有帮助吗?

5 个答案:

答案 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并且想要“动态”地转换它(没有开关),你可以对不需要不同代码的两个操作做什么操作。

我想我是说如果你因为想要访问两个对象(不同方法)不同的东西而必须进行强制转换,那么如何在没有开关的情况下实际访问那个不同的方法?

一个例外可能是内在变量 - 对于那些你想要泛型的人来说,但是在类之外使用内部变量无论如何都是一个坏主意。

哦,你真正想要的是让所有的类都实现相同的接口 - 然后你就不用了。

施法应该非常罕见。