【问题标题】:How to "dynamically" cast an instance of Object type into its specific data type?如何“动态”将 Object 类型的实例转换为其特定的数据类型?
【发布时间】:2011-04-11 05:19:20
【问题描述】:
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 反射有帮助吗?

【问题讨论】:

    标签: java reflection types casting dynamic


    【解决方案1】:

    高效且干净地处理此问题的最佳方法是让 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();
    }
    

    【讨论】:

      【解决方案2】:

      基本上,您希望在执行时执行重载解决方案 - 您将无法非常简单地做到这一点。

      某些情况下,visitor pattern 可以提供帮助,但我认为这里不会。我认为你被困在或者你在这里得到的代码,或者反射。我从来没有像我的一些同事那样热衷于访问者模式 - 总感觉有点乱 - 但值得考虑。

      您能否让foo 调用正确的doSomething 重载而不是只返回值?这就是知道正在构造什么的代码——如果你可以通过适当的重载传递一个对象来调用doSomething,你最终会在一个地方得到特定类型的逻辑。

      在 Java 7 中,invokedynamic 可能在这种情况下很有用 - 当然 C# 4 中的 dynamic 类型会有所帮助 - 但我还没有研究足够的 invokedynamic 来确定。

      【讨论】:

      • 据我所知,Java 语言没有任何方法可以真正使用 invokedynamic 字节码。不过,我想在这件事上被证明是错误的。
      • @Joachim:啊,我认为这是 Java 7 中提议的语言更改之一。老实说,我已经忘记了其中的内容和内容......
      【解决方案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 返回了什么样的 Thing。

      【讨论】:

      • foo 的返回类型必须是 Thingy。 :)
      【解决方案4】:

      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.
      

      【讨论】:

        【解决方案5】:

        在 Java 中这样做没有任何意义。 Java 是静态类型的,如果您要动态转换它,则必须有一个 switch 语句来执行此操作,以便在不同对象上调用不同的方法。

        示例——如果你有一个字符串或一个 int 并且你想“动态地”转换它(没有开关),你可以对它们执行什么操作而不需要不同的代码。

        我想我的意思是,如果您必须进行强制转换,因为您想访问两个对象的不同之处(不同的方法),那么您如何在没有开关的情况下实际访问该不同的方法?

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

        哦,你可能真正想要的是让所有类实现相同的接口——然后你就不用强制转换了。

        铸造应该是极其罕见的。

        【讨论】:

          猜你喜欢
          • 2012-08-27
          • 2013-05-20
          • 1970-01-01
          • 1970-01-01
          • 2018-11-12
          • 2023-04-10
          • 1970-01-01
          • 2016-06-02
          相关资源
          最近更新 更多