【问题标题】:Casting to a Class which is determined at run-time转换为在运行时确定的类
【发布时间】:2010-09-27 17:07:43
【问题描述】:

我有一个方法fetchObjects(String),它预计会返回一个Contract 业务对象的数组。 className 参数告诉我应该返回什么样的业务对象(当然这在这种解释的情况下没有意义,因为我已经说过我会返回Contracts,但这基本上是我真实的情况设想)。所以我从某处获取条目集并加载集合条目的类(其类型由className 指定)。

现在我需要构造返回的数组,所以我使用SettoArray(T[])方法。使用反射,我为自己构建了一个空的 Contracts 数组。 但是, 这给了我一个静态类型的值Object!所以接下来我需要将其转换为适当的类型,在本例中为Contract[](请参阅下面清单中的“星号下划线”部分)。

我的问题是:有没有办法以及如何像我在清单中那样转换为Contract[]但只能通过className确定数组元素的类型(Contract) /em>(或entriesType)?换句话说,我想要做的基本上是这样的:(entriesType[]) valueWithStaticTypeObject,其中 entriesType 被通过classname 参数指定的类替换,即Contract

这在某种程度上是不可能的,还是可以通过某种方式完成?也许使用泛型?

package xx.testcode;

import java.util.HashSet;
import java.util.Set;

class TypedArrayReflection {

    public static void main(String[] args) {
        try {
            Contract[] contracts = fetchObjects("Contract");
            System.out.println(contracts.length);
        } catch (ClassNotFoundException e) {}
    }

    static Contract[] fetchObjects(String className) throws ClassNotFoundException {
        Class<?> entriesType = Class.forName("xx.testcode."+className);
        Set<?> entries = ObjectManager.getEntrySet(className); 
        return entries.toArray( 
                (Contract[]) java.lang.reflect.Array.newInstance(
                /********/          entriesType, entries.size()) );
    }
}

class Contract { } // business object

class ObjectManager {
    static Set<?> getEntrySet(String className) {
        if (className.equals("Contract"))
            return new HashSet<Contract>();
        return null; // Error
    }
}

谢谢。


更新: 使用取自CodeIdol 的类型安全方法toArray,我更新了我的fetchObjects 方法:
static Contract[] fetchObjects(String className) throws ClassNotFoundException {
    Class<?> entriesType = Class.forName("xx.testcode."+className);
    Set<?> entries = ObjectManager.getEntrySet(className);
    return toArray(entries, entriesType); // compile error
    // -> "method not applicable for (Set<capture#3-of ?>, Class<capture#4-of ?>)"
}

public static <T> T[] toArray(Collection<T> c, Class<T> k) {
    T[] a = (T[]) java.lang.reflect.Array.newInstance(k, c.size());
    int i = 0;
    for (T x : c)
        a[i++] = x;
    return a;
}

我需要做些什么来摆脱评论中引用的编译器错误?我是否必须在 getEntrySet 方法的返回类型中指定 Set&lt;Contract&gt; 才能正常工作?感谢您的任何指点。

【问题讨论】:

  • entryType类不是Contract的子类怎么处理?
  • entriesType 不一定是 Contract 的子类型。它可以是宠物、朋友、爱好——任何你可能收藏的东西。在我的示例中,客户将拥有一系列合同。

标签: java generics reflection casting reification


【解决方案1】:

您可以使用类作为参数而不是类名。

   static <T extends Contract> T[] buildArray(Class<T> clazz){
     ArrayList<T> l=new ArrayList<T>();
     return l.toArray((T[]) java.lang.reflect.Array.newInstance(clazz, l.size()));
   }

编辑:(阅读杨评论后)

不,您不能将泛型类型与变量的值一起使用。

【讨论】:

  • 这令人印象深刻!然而问题是我不想 想硬编码指定 Contract 类(业务对象也可以是 House 或 Pet,这取决于方法参数 className)。正确的类需要根据className参数来确定。
  • 既然调用者必须知道他期望什么类型的对象,那么为什么不传入 Class 对象而不是名称呢?我不明白这个名字有什么好处。
  • 参见。我对 Milhous 的回答的评论。
【解决方案2】:

那么为什么不使用

static <T> T[] buildArray(Class<T> clazz){
ArrayList<T> l=new ArrayList<T>();
return l.toArray((T[]) java.lang.reflect.Array.newInstance(clazz, l.size()));
}

注意。修改了上面的代码。

【讨论】:

  • 我想他可能希望泛型 基于“String className”的值,没有类型/泛型/类参数。
  • 没错。在实际场景中,我需要使用反射来获取类型的名称,例如我从反射中得到“合同”,然后用这个名字加载类。
  • 您不能将类型转换为编译时未知的类型。类型转换是一个编译时问题。泛型只是一种参数化类型转换的方法,让编译器将类型信息从代码的一部分传播到另一部分。但它仍然需要编译器知道。
猜你喜欢
  • 2012-03-14
  • 1970-01-01
  • 1970-01-01
  • 2014-05-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-26
相关资源
最近更新 更多