java.lang.reflect.Array的getter和setter方法的目的是什么?

6
Java类java.lang.reflect.Array提供了一组工具,用于动态创建数组。然而,除此之外,它还有一整套方法来访问(获取、设置和长度)数组。我不明白这样做的意义,因为在创建时可以将动态生成的数组强制转换为数组,这意味着您可以使用正常的数组访问(括号表示法)功能。事实上,从源代码中可以看出,这就是该类所做的全部内容,即将数组强制转换,并在转换失败时抛出异常。

那么所有这些额外方法的作用/用处是什么?

更新

考虑到您需要事先知道数组的类型才能知道要使用哪种方法,所有基本get*()set*()方法似乎尤其没有帮助。

更新2

感谢大家的参与,你们的意见非常有启发性!我并不真正清楚除了newInstance()(也许还有getLength())之外什么时候会使用这个类,但是现在我意识到这些其他方法仍然非常有用。

"get()方法返回一个对象...你可以对一个对象做很多事情..." - mdma
3个回答

8

这个类相当深奥 — 大多数数组的用法都知道数组的类型,所以在实现处理数组通用代码时,此类通常是最有用的。

没有一个适用于所有数组的数组超类,因此无论类型如何,访问元素或数组的大小的统一方式都不存在。 java.lang.reflect.Array填补了这一空白,并允许您以相同的方式访问数组,而不受类型的限制。例如,要从任何数组中获取给定索引处的值(作为对象返回)。

这是参数多态性。当然,如果您知道类型,可以自己编写此代码 - 只需进行强制转换即可。如果您不知道数组类型,或者它可以有几种类型,那么您将检查可能性并适当进行转换 - 这就是reflect.Array中的代码所做的。

编辑:针对评论回复。考虑如何解决这个问题 - 如何计算数组中重复值的次数。如果没有类型不可知的Array类,那么在不显式转换数组的情况下,将无法编写此代码,因此您需要针对每种数组类型使用不同的函数。在这里,我们有一个处理任何类型的数组的函数。

public Map<Object, Integer> countDuplicates(Object anArray)
{
    if (!anArray.getClass().isArray())
        throw new IllegalArgumentException("anArray is not an array");

    Map<Object,Integer> dedup = new HashMap<Object,Integer>();
    int length = Array.getLength(anArray);
    for (int i=0; i<length; i++)
    {
        Object value = Array.get(anArray, i);         
        Integer count = dedup.get(value);
        dedup.put(value, count==null ? 1 : count+1);
    }
    return dedup;
}

编辑2:关于get*()和set*()方法。上面的源代码链接指向Apache harmony。那里的实现不遵守Sun Javadocs。例如,从getInt方法中

@throws IllegalArgumentException If the specified object is not an array, 
or if the indexed element cannot be converted to the return type 
by an identity or widening conversion 

这意味着实际的数组可以是byte[]short[]int[]。但是Harmony实现并非如此,它只接受int[]。(顺便提一下,Sun实现大多数Array类使用本地方法。)get*()set*()方法存在的原因与get()getLength()相同——提供松散类型的数组访问。可能不是每天都需要使用,但我想它对那些需要它的人提供了价值。

我不明白你的意思,标准的括号符号表示法适用于所有类型,包括原始类型。正如你所注意到的,它返回一个对象,因此最终仍然需要进行强制转换 - 无论是对象还是数组。因此,为什么不直接对数组进行强制转换呢? - dimo414
有时您并不关心类型,不需要强制转换,例如,在数组中计算重复值。我已经更新了我的答案,并附上了代码示例。 - mdma
然而,原始的get/set方法似乎是无用的,不是吗? - dimo414
哇,不知怎么的,我从来没有意识到docjar不是Sun的实现! - dimo414
感谢您帮助我理解这个工具的实用性。 - dimo414
没问题。这很不寻常,但并非完全无用。(我以前从未需要使用过它。) - mdma

2

关于 set...() 方法,它们确实有一些用处:

Object a = new byte[1], b = new short[1], c = new int[1], d = new long[1];
Array.setByte(a, 0, (byte)1);
Array.setByte(b, 0, (byte)1);
Array.setByte(c, 0, (byte)1);
Array.setByte(d, 0, (byte)1);

例如,您不需要知道处理的数组是int[]还是long[],就可以将值设置为5

编辑 我更改了示例,希望能够演示set...()方法允许您不必静态地知道原始数组的类型。


byte[] b = new byte[1]; short[] s = new short[1]; int[] i = new int[1]; long[] l = new long[1]; b[0] = (byte)1; s[0] = (byte)1; i[0] = (byte)1; l[0] = (byte)1;同样适用... - dimo414
哎,它搞砸了我的换行。 - dimo414
@dimo414:你在这一切中都错了重点。除非是数组,否则不能使用[]运算符。当它是基元数组时,除非知道基元类型,否则不能将其强制转换为数组。因此,如果您正在编写应接受byte []short []long []int []并将第一个元素设置为5的库函数,则必须进行四个instanceof检查以确定类型,然后将其转换为各自的原始数组,然后执行赋值。Array.setByte(someArrayRef, 0, (byte) 5)可以做到同样的事情,只需要一行代码。 - Mark Peters
我明白了。我想我一开始很难理解为什么会出现这种情况,但现在更加清楚了。 - dimo414

2
这些方法可以帮助您解除检查和强制转换数组的负担,如果您只知道“该对象是某种(可能是基本)类型的数组”。我自己只在框架和实用程序中看到过这种用法,例如commons-beanutils。这个框架以反射的方式访问Java bean的属性,并使用Array方法访问数组属性的元素。

你能提供一个例子吗?我下载了commons-beanutils的源代码,并查找Array类的引用,但没有看到任何内容。 - dimo414
一个针对 java.lang.reflect.Array 的 grep 应该返回一些结果。看看 org.apache.commons.beanutils 包中的 BeanUtilsBeanPropertyUtilsBean - Christian Semrau
嘿,我猜Windows的“全文搜索”并不是真正的全文搜索。谢谢。 - dimo414
呵呵,这正是我的发现。我找到了一篇 微软知识库文章,似乎解释了这种行为(至少对于XP而言):简而言之,Windows文件搜索“过于聪明”,无法执行全文搜索。 - Christian Semrau

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接