加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

简化此通用方法以连接Java数组

发布时间:2020-12-15 04:56:52 所属栏目:Java 来源:网络整理
导读:我的目标是实现一个方法,将任意数量的数组连接到它们的公共超类型的单个数组中,返回生成的(类型化的)数组.我有两个实现. 第一个(这个不需要简化): public static T T[] concatArrays(ClassT type,T[]... arrays) { int totalLen = 0; for (T[] arr: arrays)
我的目标是实现一个方法,将任意数量的数组连接到它们的公共超类型的单个数组中,返回生成的(类型化的)数组.我有两个实现.

第一个(这个不需要简化):

public static <T> T[] concatArrays(Class<T> type,T[]... arrays) {
    int totalLen = 0;
    for (T[] arr: arrays) {
        arr.getClass().getCom
        totalLen += arr.length;
    }
    T[] all = (T[]) Array.newInstance(type,totalLen);
    int copied = 0;
    for (T[] arr: arrays) {
        System.arraycopy(arr,all,copied,arr.length);
        copied += arr.length;
    }
    return all;
}

让我们创建一些数组:

Long[] l = { 1L,2L,3L };
Integer[] i = { 4,5,6 };
Double[] d = { 7.,8.,9. };

我们的方法调用:

Number[] n = concatArrays(Number.class,l,i,d);

这是有效的,并且是完全类型安全的(例如,concatArrays(Long.class,d)是编译器错误),但如果没有必要,指定Number.class有点烦人.所以我实现了以下方法(这是我想要简化的方法):

public static <T> T[] arrayConcat(T[] arr0,T[]... rest) {
    Class commonSuperclass = arr0.getClass().getComponentType();
    int totalLen = arr0.length;
    for (T[] arr: rest) {
        totalLen += arr.length;
        Class compClass = arr.getClass().getComponentType();
        while (! commonSuperclass.isAssignableFrom(compClass)) {
            if (compClass.isAssignableFrom(commonSuperclass)) {
                commonSuperclass = compClass;
                break;
            }
            commonSuperclass = commonSuperclass.getSuperclass();
            compClass = compClass.getSuperclass();
        }
    }
    T[] all = (T[]) Array.newInstance(commonSuperclass,totalLen);
    int copied = arr0.length;
    System.arraycopy(arr0,copied);
    for (T[] arr: rest) {
        System.arraycopy(arr,arr.length);
        copied += arr.length;
    }
    return all;
}

从客户的角度来看,这更好用:

Number[] n = arrayConcat(l,d);

而且,编译器足够聪明,可以在Long [] all = arrayConcat(l,d)上给出适当的错误.由于编译器能够识别此错误,因此很明显我在运行时执行工作(确定给定数组的公共超类),编译器能够在编译时执行.有没有办法实现我的方法而不使用基于反射的方法来确定数组创建步骤的公共超类?

我试过这种方法:

public static <T> T[] arrayConcat(T[]... arrays) {
    int totalLen = 0;
    for (T[] arr: arrays) {
        totalLen += arrays.length;
    }
    Object[] all = new Object[totalLen];
    int copied = 0;
    for (T[] arr: arrays) {
        System.arraycopy(arr,arr.length);
        copied += arr.length;
    }
    return (T[]) all;
}

但是这会在返回时抛出ClassCastException.显然新T [totalLen]也出局了.有没有人有任何其他想法?

解决方法

你可以这样做:

public static <T> T[] arrayConcat(T[]... arrays) {
    int totalLen = 0;
    for (T[] arr: arrays) {
        totalLen += arr.length;
    }
    T[] all = (T[])Array.newInstance(
        arrays.getClass().getComponentType().getComponentType(),arr.length);
        copied += arr.length;
    }
    return all;
}

这利用了以下事实:当使用varargs时,编译器构造组件的数组,并且正确设置数组的类型,使得组件类型是vararg元素类型.在这种情况下,数组的类型为T [] [],因此我们可以提取T并使用它来构造我们的T [].

(一个例外是如果调用者使用泛型类型作为varargs类型调用它,则编译器无法构造正确的数组类型.但是,如果调用者执行此操作,它将在调用者代码中生成警告(臭名昭着的varargs泛型警告),所以来电者被警告会发生坏事,所以这不是我们的错.)

这个解决方案的一个令人惊奇的事情是即使用户传递零数组也不会产生错误的答案! (只要编译器成功编译它,它就会推断(或明确指定)一些具体的类型T,使得T []是一个有效的返回类型.那个类型T以数组的形式给我们)显然在这种情况下,编译器无法正确推断.

请注意,调用者可以手动传递“arrays”参数,在这种情况下,它可以具有运行时类型U [] [],其中U是T的子类型.在这种情况下,我们的方法将返回一个数组运行时类型U [],它仍然是正确的结果,因为U []是T []的子类型.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读