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

Posted

技术标签:

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

【问题讨论】:

【参考方案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 并且你想“动态地”转换它(没有开关),你可以对它们执行什么操作而不需要不同的代码。

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

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

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

铸造应该是极其罕见的。

【讨论】:

以上是关于如何“动态”将 Object 类型的实例转换为其特定的数据类型?的主要内容,如果未能解决你的问题,请参考以下文章

java中如何把一个String类型的变量转换成double型的?

javascript怎样将object类型转换成array数组

VB中如何将object类型转换为其他类型?

如何将object转换成int

如何在 Vue 2 和 TypeScript 中将 $refs 字段转换为其组件类型?

将对象的实例克隆为其基本类型