Несколько универсальных типов - PullRequest
1 голос
/ 29 мая 2019

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

У меня вопрос, есть ли способ иметь несколько возможных расширений для дженериков.Например:

public class Foo<? extends String>{}
public class Bar<? extends StringBuilder>{}
//combined
public class FooBar<? extends String, StringBuilder>{}
//or perhaps
public class FooBar<? extends String || StringBuilder>{}

Я знаю, что класс FooBar не будет компилироваться, но я надеюсь, что это полезно для объяснения моего вопроса и указанного намерения.И наконец, еще раз повторю мой вопрос: возможно ли расширение двух классов в обобщенном предложении или способ, который косвенно имитирует такое действие?

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

Ответы [ 2 ]

3 голосов
/ 29 мая 2019

Невозможно расширить класс как String, так и StringBuilder, поскольку у классов есть только один родительский класс.Тем не менее, они могут реализовывать несколько интерфейсов, которые вы можете указать с помощью &.

class FooBar<T extends String & Runnable & Collection<Integer>> {}

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

1 голос
/ 29 мая 2019

Итак, то, что вы спрашиваете, невозможно из коробки, как сказал Джон, но вы все равно можете добиться аналогичного поведения, используя так называемый тип Either, который используется для представления ситуации, когда вы можете выбрать один из двух типов.

Вы можете легко найти полностью реализованный класс Either с помощью простого поиска в Google, например этот на github

Ниже приведен фрагмент кода:

public class Either<L, R> {
    private final L left;
    private final R right;
    private final boolean isRight;

    private Either(L left, R right, boolean isRight) {
        this.left = left;
        this.right = right;
        this.isRight = isRight;
    }

    public static <L, R> Either<L, R> left(L left){
        return new Either<>(left, null, false);
    }

    public static <L, R> Either<L, R> right(R right){
        return new Either<>(null, right, true);
    }

    public <T>  T fold(Function<L,T> foldLeft, Function<R, T> foldRight){
        return isRight ? foldRight.apply(right) : foldLeft.apply(left);
    }
}

Теперь предположим, что у вас есть интерфейс с каким-то методом, который должен принимать String или StringBuilder:

public interface IFooBar <T extends Either<? extends String, ? extends StringBuilder>>{
    String  doSomething(T t);
}

И реализация:

class FooBar implements IFooBar<Either<String, StringBuilder>> {
    @Override
    public String doSomething(Either<String, StringBuilder> either) {
        return either.fold(s -> "String: " + s, sb -> "StringBuilder:" + sb);
    }
}

Тогда вы можете просто использовать его так:

public static void main(String[] args) {
    IFooBar<Either<String, StringBuilder>> fooBar = new FooBar();
    // Since in this case it is single method interface you can even use lambda expression
    // IFooBar<Either<String, StringBuilder>> fooBar = either -> either.fold(s -> "String: " + s, sb -> "StringBuilder:" + sb);
    System.out.println(fooBar.doSomething(Either.left("Foo")));
    System.out.println(fooBar.doSomething(Either.right(new StringBuilder("Bar"))));
}

Надеюсь, это вам поможет.

...