смешивать переменные универсального типа для реализации функции отображения типов в Java - PullRequest
4 голосов
/ 28 июля 2010

Я хочу написать безопасный для типа метод map в Java, который возвращает коллекцию того же типа, что и передаваемый аргумент (т.е. 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;
}

, но это не работает, потому что компилятор не ожидает, что переменные универсального типа станут более специализированными (я думаю).

Любая идея о том, как это сделатьработа

Ответы [ 4 ]

2 голосов
/ 29 июля 2010

AFAIK, в Java нет способа сделать это, так что он (а) безопасен для компиляции и (б) вызывающий map не должен повторять тип сбора источника и цели. Java не поддерживает виды высшего порядка , то, что вы просите, может быть достигнуто в Scala 2.8, но даже там детали реализации несколько сложны, см. этот вопрос SO .

1 голос
/ 28 июля 2010

Кажется, что нельзя использовать универсальные типы в универсальных типах. Так как вам нужно только ограниченное количество из них, вы можете просто перечислить их:

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").

Интересный вопрос между прочим.

0 голосов
/ 28 июля 2010

Это работает:

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);
}
0 голосов
/ 28 июля 2010

То, что вы хотите сделать, невозможно.Однако вы можете указать универсальный тип возвращаемой коллекции.

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;
}

Это возвращает коллекцию того типа, который вы указали в аргументе, просто не делает этого явно.Однако безопасно привести метод обратно к типу коллекции вашего аргумента.Однако вы должны заметить, что код не будет работать, если тип передаваемой вами коллекции не имеет конструктора без аргументов.

Затем вы можете использовать его следующим образом:

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);
...