Составление двух функций с разными типами исключений (java generics) - PullRequest
0 голосов
/ 27 апреля 2018

Допустим, я хочу реализовать версию java.util.function.Function<T,R>, которая может выдавать какой-то тип исключения E. Я создал функциональный интерфейс с именем ThrowingFunction примерно так:

@FunctionalInterface
interface ThrowingFunction<T, R, E extends Exception>
{
    R apply(T t) throws E;
}

Теперь, как и в java.util.function.Function<T,R>, мне нужно реализовать метод по умолчанию compose, и мне нужно составить два разных ThrowingFunction экземпляра и, вероятно, они могут иметь другой тип исключения броска . Итак, вот моя попытка:

default <V, E1 extends Exception, E2 super E1 & E> ThrowingFunction<V, R, E2> compose(ThrowingFunction<? super V, ? extends T, ? extends E1> before)
{
    return v -> apply(before.apply(v));
}

Здесь, конечно, я получаю сообщение об ошибке компилятора:

Синтаксическая ошибка на токене "super", расширяет ожидаемый

Именно в объявлении универсального параметра E2 super E1 & E.

Итак, каковы возможные решения этой проблемы, просто составить два разных ThrowingFunction экземпляра, имеющих один и тот же тип исключения E (, и это не то, что желательно )?

1 Ответ

0 голосов
/ 27 апреля 2018

Вы не можете использовать метод default здесь; это должен быть метод static на интерфейсе. Когда вы пытаетесь использовать метод default, тип E уже захвачен, и компилятор Java не позволит вам объявить тип E2 как супертип чего-либо намного меньшего, чем 2 типа E и E1, как показывает ошибка вашего компилятора.

При переходе на метод static вы можете объявить параметры типа исключения по своему усмотрению. Вы можете объявить тип исключения супертипа S, за которым следуют типы исключений подтипа E1 и E2. Остальное следует аналогично методу Function по умолчанию compose.

Параметры типа здесь соответствуют тому, что использует Function.compose, с добавлением параметров типа, связанных с исключением.

@FunctionalInterface
interface ThrowingFunction<T, R, E extends Exception>
{
    R apply(T t) throws E;

    /**
     * Composes f (after) and g (before) to produce the composed
     * function f o g(v), which executes f(g(v)).
     * @param f The function that will take g's output <T> as input to return the overall result <R>.
     * @param g The function that will take the overall input <V> and return f's input <T>.
     * @param <V> The input result type of the entire composed function (and of function g).
     * @param <T> The result type of g, used as input to f.
     * @param <R> The end result type of the entire composed function (and of function f).
     * @param <S> If any of f or g throws an exception, it is captured by the supertype exception class S.
     * @param <E1> The exception type thrown by f.
     * @param <E2> The exception type thrown by g.
     * @return A ThrowingFunction that whose input <V> is applied to g, whose result is passed to
     *     f to generate the overall result <R>.
     */
    static <V, T, R, S extends Exception, E1 extends S, E2 extends S> ThrowingFunction<V, R, S>
        compose(ThrowingFunction<? super T, ? extends R, ? extends E1> f,
                ThrowingFunction<? super V, ? extends T, ? extends E2> g)
    {
        return v -> f.apply(g.apply(v));
    }
}
...