Как верхний-связанный самореферентный тип?

У меня есть вещи (скажем, для контекста, числа), которые могут выполнять операции по своему типу:

interface Number<N> {
    N add(N to);
}
class Int implements Number<Int> {
    Int add(Int to) {...}
}

и актеров, которые действуют на все подтипы определенной верхней границы:

interface Actor<U> {
    <E extends U> E act(Iterable<? extends E> items);
}

Я хочу сделать актера, который действует полиморфно на любой числовой тип:

class Sum implements Actor<Number> {
    <N extends Number<N>> N act(Iterable<? extends N> items) {...}
}

Теперь, очевидно, это не работает, потому что Number и Number<N> не совпадают. Фактически, поскольку Number не ограничивает параметр типа конструктора как своего собственного типа, такой актер не может работать. Но я вообще не хочу работать с Number в целом - я доволен своей функциональностью работать только на Numbers некоторого типа N extends Number<N>

В качестве альтернативы я могу объявить:

interface Actor<E> {
    E act(Iterable<? extends E> items);
}

class Sum<N extends Number<N>> implements Actor<N> {
    N act(Iterable<? extends N> items) {...}
}

Но это не работает для меня, потому что он заставляет меня знать N, когда я создаю свой Sum, который мне не подходит. Это также приводит к уродливому <N extends Number<N>> для каждого класса или метода, который полиморфно использует a Sum, вызывая пролиферацию типа беспорядка.

Есть ли какой-нибудь элегантный способ сделать то, что я хочу?

Пример:

Вот пример кода, выражающий то, что я хотел бы сделать.

interface Folder<U> {
    <E extends U> E fold(Iterable<? extends E> items);
}

class Sum implements Folder<Number> {
    <N extends Number<N>> N fold(Iterable<? extends N> items) {
        Iterator<? extends N> iter = items.iterator();
        N item = iter.next();
        while (iter.hasNext())
            item = item.add(iter.next());
        return item;
    }
}

class Concat implements Folder<String> {
    <S extends String> fold(Iterable<? extends S> items) {
        StringBuilder concatenation = new StringBuilder();
        for (S item : items)
            concatenation.append(item);
        return concatenation.toString();
    }
}

class FoldUtils {
    static <U, E extends U> E foldDeep(Folder<U> folder, Iterable<? extends Iterable<? extends E>> itemses) {
        Collection<E> partialResults = new ArrayList<E>();
        for (Iterable<? extends E> items : itemses)
            partialResults.add(folder.fold(items));
        return folder.fold(partialResults);
    }
}

Ответ 1

Глядя на ваш пример, я не уверен, что вы получаете от того, что общий метод предоставляет конкретный параметр и его наличие в актере:

class Sum<T extends Number<T>> implements Actor<T> {
    T act(Iterable<? extends T> items) {...}
}

Какова добродетель наличия Sum<any-self-referential-Number> против просто наличия Sum<Int> и a Sum<Float> и т.д.

Если вы беспокоитесь о незначительной накладной памяти для создания разных экземпляров, вы можете просто возвращать один и тот же экземпляр каждый раз с помощью непроверенного броска, как это обычно бывает в безопасном режиме (см., например, Guava Optional.absent() или Collections.emptyList()).

В вашем примере кому-то в конце концов придется делать:

List<List<Int>> list;
foldDeep(new Sum(), list)

Итак, почему бы просто не потребоваться параметр типа?

foldDeep(new Sum<Int>(), list)

Или, если он заключен за factory,

foldDeep(Sum.instance(), list)
foldDeep(NumberFolders.sum(), list)

Короче говоря, мне не ясно, почему это не будет работать так же хорошо:

interface Folder<U> {
    U fold(Iterable<? extends U> items);
}

class Sum<T extends Number<T>> implements Folder<T> {
    public T fold(Iterable<? extends T> items) {
        //...
    }
}

class FoldUtils {
    static <E> E foldDeep(Folder<E> folder, Iterable<? extends Iterable<? extends E>> itemses) {
        Collection<E> partialResults = new ArrayList<>();
        for (Iterable<? extends E> items : itemses)
            partialResults.add(folder.fold(items));
        return folder.fold(partialResults);
    }
}

//...
FoldUtils.foldDeep(new Sum<>(), list);