Подтвердить что ты не робот

Вывод типичных типов вложенных статических общих функций

Является ли компилятор Java способным вывести тип общей статической функции из своего контекста в качестве аргумента в другую общую статическую функцию?

Например, у меня есть простой класс Pair:

public class Pair<F, S> {

    private final F mFirst;

    private final S mSecond;

    public Pair(F first, S second) {
        mFirst  = checkNotNull(first);
        mSecond = checkNotNull(second);
    }

    public static <F, S, F1 extends F, S1 extends S> Pair<F, S> of(F1 first, S1 second) {
        return new Pair<F, S>(first, second);
    }

    public F first() {
        return mFirst;
    }

    public S second() {
        return mSecond;
    }

    // ...
}

И у меня есть следующая общая статическая функция:

public static <F, P extends Pair<F, ?>> Function<P, F> deferredFirst() {
    return (Function<P, F>)DEFERRED_FIRST;
}

private static final Function<Pair<Object, ?>, Object> DEFERRED_FIRST = 
        new Function<Pair<Object,?>, Object>() {

    @Override
    public Object apply(Pair<Object, ?> input) {
        return input.first();
    }
};

Что я хотел бы использовать следующим образом (Collections2.transform от Google Guava):

List<Pair<Integer, Double>> values = ...
Collection<Integer> firsts = Collections2.transform(values, 
        Pair.deferredFirst());

Кому компилятор жалуется:

The method transform(Collection<F>, Function<? super F,T>) in the type 
Collections2 is not applicable for the arguments 
(List<Pair<Integer,Double>>, Function<Pair<Object,?>,Object>)

Таким образом, кажется, что компилятор не может распространять типы, выводимые для transform(), на deferredFirst(), поскольку он считает, что они являются объектами.

Принуждение компилятора к пониманию типов одним из этих способов работает:

Function<Pair<Integer, ?>, Integer> func = Pair.deferredFirst();
Collection<Integer> firsts = Collections2.transform(values, func);


Collection<Integer> firsts = Collections2.transform(values, 
        Pair.<Integer, Pair<Integer, ?>>deferredFirst());

Можно ли изменить сигнатуру функции, чтобы позволить компилятору выводить/распространять типы?

Изменить: Для богемского языка, здесь возможный метод, приведенный выше пример может быть использован в:

public static int sumSomeInts(List<Pair<Integer, Double>> values) {
    Collection<Integer> ints = Collections2.transform(values, 
            Pair.deferredFirst());
    int sum = 0;
    for(int i : ints)
        sum += i;
    return sum;
}
4b9b3361

Ответ 1

Тип вывода является неприятным и сложным. Они должны где-то остановиться. Рассмотрим

static <T> T foo();

String s = foo();

print( foo() )

В контексте назначения намерение программиста ясно, T должно быть String

В следующей строке не так много.

Метод print не очень хороший пример, он сильно перегружен. Предположим, что print не перегружен, его тип параметра фиксирован, поэтому T можно сделать ясно. Должен ли компилятор быть достаточно умным, чтобы понять это?

Это звучит разумно, пока вы не захотите прочитать соответствующий спецификационный текст, 15.12 Выражения вызова метода Удачи, что-то изменилось в этом беспорядке!

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

Ответ 2

Попробуйте использовать эти генерики kung fu:

public static int sumSomeInts(List<Pair<Integer, Double>> values) {
    Collection<Integer> ints = Collections2.transform(values, 
        Pair.<Integer, Double>deferredFirst());
    int sum = 0;
    for(int i : ints)
        sum += i;
    return sum;
}

Вы можете ввести вызов метода и передать генераторы до следующего вызова.

Я не уверен в том, какие именно общие параметры используются здесь, потому что вы не включили достаточно кода. Если вы вставляете весь метод, где проблема, я отредактирую этот ответ, чтобы он скомпилировался. EDITED: с новой информацией из вопроса

Пожалуйста, дайте мне знать, если он скомпилируется. Если это не решение, оно будет близко. Ключ состоит в том, чтобы ввести статический метод, используя синтаксис Class.<Type>staticMethod().

Ответ 3

То, что я недавно придумал, это:

@SuppressWarnings("rawtypes")
private static final Function ExtractFirst = new Function() {
    @Override
    public Object apply(final Object from) {
        Preconditions.checkNotNull(from);
        return ((Pair)from).first;
    }
};

@SuppressWarnings("unchecked")
public static <A> Function<Pair<A,?>,A> extractFirst() {
    return ExtractFirst;
}

Не позволяйте "SuppressWarnings" отключить вас, он отлично работает.

Пример:

List<Pair<String,String>> pairs = ImmutableList.of(Pair.of("a", "b"),
    Pair.of("c", "d"),Pair.of("e", "f"));
Iterable<String> firsts = Iterables.transform(pairs,
    Pair.<String>extractFirst());

К сожалению, да, вы должны предоставить общий аргумент extractFirst(). Я думаю, что это лучшее, что вы получите.