安全地将非通用Collection转换为同类通用Collection

时间:2018-07-31 16:06:39

标签: java generics

我正在使用类似的方法将非通用Collection安全地转换为同类通用Collection,即立即抛出ClassCastException:

public static <T> Collection<T> safeCastCollection(final Collection collection, final Class<T> targetClass) {
    if (collection == null) {
        return null;
    }
    collection.forEach(targetClass::cast);
    return (Collection<T>) collection;
}

现在,让该方法不返回Collection,而是与输入参数具有相同基本类型(Set,List等)的collection会很好。我能做的最好的是:

public static <T, C extends Collection<T>, I extends C> C safeCastCollection2(final I collection, final Class<T> targetClass) {
    if (collection == null) {
        return null;
    }
    collection.forEach(targetClass::cast);
    return (C) collection;
}

这在大多数情况下都适用,除了最后一种:

List listOfStrings = Arrays.asList(new String[]{"a", "b", "c"});
Set setOfNumbers = new HashSet(Arrays.asList(new Integer[]{1, 2, 3}));

Collection<String> l0 = safeCastCollection(listOfStrings, String.class);
// Collection<Integer> l1 = safeCastCollection(listOfStrings, String.class); // compiler error :-)
// List<String> l2 = safeCastCollection(listOfStrings, String.class); // compiler error :-(

Collection<String> l3 = safeCastCollection2(listOfStrings, String.class);
// Collection<Integer> l4 = safeCastCollection(listOfStrings, String.class); // compiler error :-)
List<String> l5 = safeCastCollection2(listOfStrings, String.class); // no compiler error :-)

Set<Number> l6 = safeCastCollection2(setOfNumbers, Integer.class);
Collection<Number> l7 = safeCastCollection2(setOfNumbers, Integer.class);
// List<Number> l8 = safeCastCollection2(setOfNumbers, Integer.class); // compiler error :-)

Collection<String> l9 = safeCastCollection2(setOfNumbers, Integer.class); // no compiler error :-(
final String next = l9.iterator().next(); // runtime exception 

有什么想法可以改善对输入参数的约束吗?

1 个答案:

答案 0 :(得分:0)

这样对您来说太冗长吗?

@SuppressWarnings("unchecked")
public static <T> Collection<T> safeCastCollection(final Collection collection, final Class<T> targetClass) {
    return (Collection<T>) typeCheckedCollection(collection, targetClass);
}

@SuppressWarnings("unchecked")
public static <T> List<T> safeCastCollection(final List list, final Class<T> targetClass) {
    return (List<T>) typeCheckedCollection(list, targetClass);
}

@SuppressWarnings("unchecked")
public static <T> Set<T> safeCastCollection(final Set set, final Class<T> targetClass) {
    return (Set<T>) typeCheckedCollection(set, targetClass);
}

// etc. for other Collection subinterfaces

@SuppressWarnings("rawtypes,unchecked")
private static Collection typeCheckedCollection(final Collection collection, final Class<?> targetClass) {
    if (collection != null) {
        collection.forEach(targetClass::cast);
    }
    return collection;
}

这将满足您的要求(请注意,我将<Number>替换为<? extends Number>,因为无法将Set<Integer>分配给Set<Number>):

    List listOfStrings = Arrays.asList(new String[]{"a", "b", "c"});
    Set setOfNumbers = new HashSet(Arrays.asList(new Integer[]{1, 2, 3}));

    Collection<String> l0 = safeCastCollection(listOfStrings, String.class);
// Collection<Integer> l1 = safeCastCollection(listOfStrings, String.class); // compiler error :-)
// List<String> l2 = safeCastCollection(listOfStrings, String.class); // compiler error :-(

    Collection<String> l3 = safeCastCollection(listOfStrings, String.class);
// Collection<Integer> l4 = safeCastCollection(listOfStrings, String.class); // compiler error :-)
    List<String> l5 = safeCastCollection(listOfStrings, String.class); // no compiler error :-)

    Set<? extends Number> l6 = safeCastCollection(setOfNumbers, Integer.class);
    Collection<? extends Number> l7 = safeCastCollection(setOfNumbers, Integer.class);
// List<Number> l8 = safeCastCollection(setOfNumbers, Integer.class); // compiler error :-)

// Collection<String> l9 = safeCastCollection(setOfNumbers, Integer.class); // compiler error :-)