【问题标题】:casting ArrayList.toArray() with ArrayList of Generic Arrays使用通用数组的 ArrayList 强制转换 ArrayList.toArray()
【发布时间】:2011-06-11 19:22:25
【问题描述】:

一个我几乎要放弃所有希望的难题。我正在尝试制作一个 函数,但是在让ArrayList.toArray() 返回我想要的类型时遇到问题。

这是演示我的问题的最小示例:

public static <T> T[] test(T one, T two) {
    java.util.List<T> list = new ArrayList<T>();
    list.add(one);
    list.add(two);
    return (T[]) list.toArray();
}

通常我可以使用(T[]) list.toArray(new T[0]) 的形式,但还有两个额外的困难:

  1. 由于协方差规则,我无法对数组进行类型转换,(T[]) myObjectArray 给出了ClassCastException
  2. 您不能创建泛型类型的新实例,这意味着我不能实例化new T[]。我也不能对 ArrayList 的元素之一使用 clone 或尝试获取它的类。

我一直在尝试使用以下调用获取一些信息:

public static void main(String[] args) {
   System.out.println(test("onestring", "twostrings"));
}

结果是[Ljava.lang.Object;@3e25a5 的形式,表明返回的类型转换无效。奇怪的是:

public static void main(String[] args) {
    System.out.println(test("onestring", "twostrings").getClass());
}

返回:

Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
at patchLinker.Utilities.main(Utilities.java:286)

所以我最好的猜测是它认为它是一个字符串数组标签,但内部是一个对象数组,任何访问尝试都会导致这种不一致。

如果有人能找到解决此问题的任何方法(因为我拒绝了这两种正常的解决方法),我将非常感激。

K.巴拉德 JDK1.6


编辑


感谢 Peter Lawrey 解决了最初的问题。现在的问题是如何使解决方案适用于我不那么琐碎的示例:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;
    T[] typeVar;
    for (T[] subArray : input) {
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }
    if (input.length > 0) {
        typeVar = input[0];
    } else {
        return null;
    }
    return (T[]) outList.toArray((T[]) java.lang.reflect.Array.newInstance(typeVar.getClass(),outList.size()));
}

public static void main(String[] args) {
    String[][] lines = { { "111", "122", "133" }, { "211", "222", "233" } };
    unTreeArray(lines);
}

此刻的结果是

Exception in thread "main" java.lang.ArrayStoreException
at java.lang.System.arraycopy(Native Method)
at java.util.ArrayList.toArray(Unknown Source)
at patchLinker.Utilities.unTreeArray(Utilities.java:159)
at patchLinker.Utilities.main(Utilities.java:301)

我仍在做一些测试,看看是否能得到比这更有用的信息,但目前我不确定从哪里开始。我会在获得时添加更多信息。

K.巴拉德


编辑 2


现在提供更多信息。我尝试手动构建数组并以元素方式传输项目,所以我将类型转换为 T,而不是 T[]。我发现了一个有趣的结果:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;
    T[] outArray;
    for (T[] subArray : input) {
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }
    if (outList.size() == 0) {
        return null;
    } else {
        outArray = input[0];// use as a class sampler
        outArray =
                (T[]) java.lang.reflect.Array.newInstance(outArray.getClass(), outList.size());
        for (int i = 0; i < outList.size(); i++) {
            System.out.println(i + "  " + outArray.length + "   " + outList.size() + "   "  + outList.get(i));
            outArray[i] = (T) outList.get(i);
        }
        System.out.println(outArray.length);
        return outArray;
    }
}

我仍然得到以下输出:

0  6   6   111
Exception in thread "main" java.lang.ArrayStoreException: java.lang.String
at patchLinker.Utilities.unTreeArray(Utilities.java:291)
at patchLinker.Utilities.main(Utilities.java:307)

这是否意味着即使您设法间接创建了T[],您也不能写信?

【问题讨论】:

  • 我建议坚持使用集合并留下老式参考数组。
  • 很愿意,但有时您在这件事上别无选择。向后兼容总是意味着某些东西是向后的。

标签: java arrays generics casting arraylist


【解决方案1】:

toArray() 总是返回一个对象数组。

要返回特定类型的数组,需要使用 toArray(Object[])。

要为数组参数提供正确的类型,您需要使用特定的类,或者在这种情况下使用反射。

试试

 return (T[]) list.toArray(Array.newInstance(one.getClass(), list.size()));

【讨论】:

  • 感谢您的快速响应。我试过了,但它给出了相当模糊的错误: List 类型中的方法 toArray(T[]) 不适用于参数(Object) 我做了一些手动检查,我可以调用 getClass()实例,所以我怀疑这是因为 newInstance() 返回一个对象
  • 您应该在 Array.newInstance 方法结果上添加强制转换。即:list.toArray((T[])Array.newInstance(one.getClass(), 0))
  • @K.Barad 它需要另一个演员表。如果两个对象是不同类型的,它也会有问题。或者如果返回的数组被调用者修改了。
  • 谢谢,我忘记了转换 Object => T[] 是有效的,只有 Object[] => T[] 不是。现在来测试它是否适用于我的(稍微不那么琐碎的)真实方法。感谢您的帮助,希望这已经解决了
  • 不走运。可能我犯了一些愚蠢的错误,我已经看到这段代码太久没有休息了。
【解决方案2】:

您的第二个代码示例的问题是由于您的T[] typeVar 引起的:输入是两个暗淡的数组,因此input[0] 将返回一个暗淡的数组(如预期的那样,是一个字符串 [],而不是一个字符串)。

如果您要将List&lt;T&gt; 转换为T[],则需要T typeVar

修复它:

public static <T> T[] unTreeArray(T[][] input) {
    java.util.List<T> outList = new ArrayList<T>();
    java.util.List<T> tempList;

    if (input.length == 0) {
        return null;
    }


    T typeVar=null;
    for (T[] subArray : input) {
        if(typeVar==null && subArray.length>0) {
            typeVar=subArray[0];
        }
        tempList = java.util.Arrays.asList(subArray);
        outList.addAll(tempList);
    }

    return outList.toArray((T[]) Array.newInstance(typeVar.getClass(),0));
}

public static void main(String[] args) {
    String[][] lines = { { "111", "122", "133" }, { "211", "222", "233" } };
    String[] result=unTreeArray(lines);

    System.out.println(result);

    System.out.println(result.getClass());

        //added for completion:
    System.out.println( Arrays.toString(result));

}

结果输出:

[Ljava.lang.String;@5a405a4 类
[Ljava.lang.String;
[111、122、133、211、222、233]

【讨论】:

  • 好吧,这绝对是我的疯狂时刻。当然,您将元素而不是数组类型传递给它,或者有什么意义?谢谢你,看来一切都正常了。
  • 经常发生。有时我们从过于接近我们自己的代码来寻找隐藏的错误:)
  • 对@TomasNarros 的问题如果输入数组为空,那么 typeVar 将保留为 NULL,因此您会遇到 NullPointerException。在这种情况下如何返回一个空列表?
  • 如果输入可以为空,您应该首先检查,将if (input.length == 0)更改为if (input !=null &amp;&amp; input.length == 0)
【解决方案3】:

您不能创建泛型类型参数的数组。原因很简单,Java 通常会在运行时擦除泛型类型信息,因此运行时不知道 T 是什么(当 T 是泛型类型参数时)。

所以解决方案是在运行时实际获取类型信息。通常这是通过传递 Class&lt;T&gt; type 参数来完成的,但在这种情况下,您可以避免这种情况:

@peter 的答案看起来不错,我会使用它:)。

return (T[]) list.toArray(Array.newInstance(one.getClass(), list.size()));

【讨论】:

  • 是的,我已经花费了几个小时以上的时间搜索,阅读了泛型无法让我获得简单解决方案的所有(诚然非常好的)原因。
猜你喜欢
  • 2012-09-04
  • 1970-01-01
  • 1970-01-01
  • 2013-05-27
  • 1970-01-01
  • 1970-01-01
  • 2013-04-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多