簡體   English   中英

混合泛型類型變量以在Java中實現類型安全的映射函數

[英]mix generic type variables to implement a type-safe map function in Java

我想在Java中編寫一個類型安全的map方法,它返回一個與傳遞的參數相同類型的Collection(即ArrayList,LinkedList,TreeSet等)但是具有不同的泛型類型(在有角度的括號之間),確定通過另一個參數的泛型類型(通用映射函數的結果類型)。

所以代碼將用作:

public interface TransformFunctor<S, R> {
    public R apply(S sourceObject);
}

TransformFunctor i2s = new TransformFunctor<Integer, String>() {
    String apply(Integer i) {return i.toString();};

ArrayList<Integer> ali = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
ArrayList<String> als = map(ali, i2s);

TreeSet<Integer> tsi = new TreeSet<Integer>(Arrays.asList(1, 2, 3, 4));
TreeSet<String> tss = map(tsi, i2s);

這個想法是這樣的:

public static <C extends Collection<?>, S, R>
C<R> map(final C<S> collection, final TransformFunctor<S, R> f)
throws Exception {
    //if this casting can be removed, the better
    C<R> result = (C<R>) collection.getClass().newInstance();
    for (S i : collection) {
        result.add(f.apply(i));
    }
    return result;
}

但這不起作用,因為編譯器不希望泛型類型變量進一步專門化(我認為)。

有關如何使這項工作的任何想法?

AFAIK在Java中無法做到這一點,因此它既是(a)編譯類型安全的,又是(b) map的調用者不需要重復源和目標的集合類型。 Java不支持更高階的類型 ,在Scala 2.8中可以實現您所要求的,但即使在那里,實現細節也有些復雜,請參閱此SO問題

似乎無法在泛型類型上使用泛型類型。 由於您只需要有限數量的那些,您可以枚舉它們:

public static <CS extends Collection<S>, CR extends Collection<R>, S, R> CR map(
        final CS collection, final TransformFunctor<S, R> f)
        throws Exception {
    // if this casting can be removed, the better
    CR result = (CR) collection.getClass().newInstance();
    for (S i : collection) {
        result.add(f.apply(i));
    }
    return result;
}

我使用CS作為源集合, CR作為結果集合。 我擔心你無法刪除演員,因為你不能在運行時使用泛型。 newInstance()只是創建一個Object類型的對象,並且為了滿足編譯器,需要轉換為CR 但它仍然是一種欺騙。 這就是編譯器發出警告的原因,你必須使用@SuppressWarnings("unchecked")來抑制。

有趣的問題btw。

你想做什么是不可能的。 但是,您可以指定返回集合的泛型類型。

public static <S, R> Collection<R> map(Collection<S> collection, TransformFunctor<S,R> f) throws Exception {
    Collection<R> result = collection.getClass().newInstance();
    for (S i : collection) {
        result.add(f.apply(i));
    }
    return result;
}

這確實返回了您使用參數指定的類型的集合,它不會顯式地這樣做。 但是,將方法返回轉換為參數的集合類型是安全的。 但是,您應該注意,如果您傳遞的集合類型沒有no-arg構造函數,則代碼將失敗。

然后你可以像這樣使用它:

ArrayList<Integer> ali = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4));
ArrayList<String> als = (ArrayList<String>) map(ali, i2s);

TreeSet<Integer> tsi = new TreeSet<Integer>(Arrays.asList(1, 2, 3, 4));
TreeSet<String> tss = (TreeSet<String>) map(tsi, i2s);

這有效:

class Test {

    public static void main(String[] args) throws Exception {
        Function<Integer, String> i2s = new Function<Integer, String>() {
            public String apply(Integer i) {
                return i.toString();
            }
        };

        ArrayList<Integer> ali = new ArrayList<Integer>(Arrays.asList(1, 2, 3,
                4));
        ArrayList<String> als = map(ali, i2s);

        TreeSet<Integer> tsi = new TreeSet<Integer>(Arrays.asList(1, 2, 3, 4));
        TreeSet<String> tss = map(tsi, i2s);
        System.out.println(""+ali+als+tss);
    }

    static <SC extends Collection<S>, S, T, TC extends Collection<T>> TC map(
            SC collection, Function<S, T> func) throws Exception {
        // if this casting can be removed, the better
        TC result = (TC) collection.getClass().newInstance();
        for (S i : collection) {
            result.add(func.apply(i));
        }
        return result;
    }

}

interface Function<S, R> {
    R apply(S src);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM