Несколько общих типов

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

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

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 не скомпилируется, но я надеюсь, что это поможет объяснить мой вопрос и указанное намерение. Наконец, повторю мой вопрос: возможно ли, чтобы два класса были расширены в общем предложении или способом, который косвенно имитировал бы такое действие?

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

Можете ли вы привести полный пример? потому что этот вопрос не совсем ясен

Emax 29.05.2019 20:12

@Emax Я не совсем уверен, что еще определить. Класс есть, дженерики есть. И намерение таково, что "?" может расширять как String, так и StringBuilder. Если бы это было так ? расширяет число, то общим может быть число, целое число, двойное число и т. д. Но цель состоит в том, чтобы иметь двух братьев и сестер, а не отношения родитель-потомок.

George Xavier 29.05.2019 20:15

Итак, если я правильно понял, вы хотите иметь общий, который будет либо из иерархии String или StringBuilder?

FilipRistic 29.05.2019 20:17

@FilipRistic правильно

George Xavier 29.05.2019 20:18
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
4
57
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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

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

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

Большое спасибо. Я предполагаю, что причина в том, что по той же причине множественное наследование для классов не разрешено?

George Xavier 29.05.2019 20:18

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

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

Для фрагмента кода ниже давайте рассмотрим эту упрощенную версию:

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

Я надеюсь, что это поможет вам.

Я попробую это. Судя по беглому взгляду, это может сработать. Спасибо.

George Xavier 29.05.2019 22:01

Другие вопросы по теме