Можно ли требовать сразу несколько типов?

В основном я хочу это сделать:

public interface A {
    void a();
}
public interface B {
    void b();
}
public class SomeClass {
    public SomeClass(<A&B> e) { // Note the type here
        e.a();
        e.b();
    }
}

То, что я сделал в строке комментария, явно незаконно. Я знаю, что могу просто потребовать от переданного объекта реализовать интерфейс A или интерфейс B, но есть ли способ сделать и то, и другое?

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

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
3
0
286
5
Перейти к ответу Данный вопрос помечен как решенный

Ответы 5

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

Вы можете сделать это с включенными дженериками. Например, чтобы принять экземпляр некоторого класса, который реализует как CharSequence, так и Appendable:

  public <T extends CharSequence & Appendable> void someMethod(T param) {
    ...
  }

Это кажется запутанным, поскольку вам все еще нужно определить тип T, который реализует как A, так и B. Почему бы просто не сделать его типом paramater? Если многие объекты реализуют A и B, почему бы не создать интерфейс C: A, B?

Joseph Daigle 13.01.2009 17:00

В зависимости от дизайна вы можете выполнить одно из следующих действий:

  1. Сделайте A: B или B: A.
  2. Сделайте интерфейс C: A, B.

В любом случае вы захотите иметь контракт, который включает как a (), так и b ().

Спасибо, но вариант наследования не всегда имеет смысл (A и B могут быть совершенно не связаны. Мне тоже не очень нравится второй вариант, потому что это означает, что 1) мне, возможно, придется сделать `` интерфейсы C '' для всех возможных комбинация интерфейсов и 2) классы должны все это реализовывать ...

Jordi 13.01.2009 11:13

Как я уже сказал, это зависит от дизайна. Если бы A и B были связаны, то 1) работал бы. 2) На самом деле грешить не так уж и плохо. A и B несколько связаны, потому что вы передаете объект, который реализует оба. А как насчет базового класса, который реализует и то, и другое?

Joseph Daigle 13.01.2009 16:59

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

public interface A {
    void a();
}
public interface B {
    void b();
}

public interface C extends A, B{}

public class SomeClass{
    public SomeClass(C e) { // Note the type here
      e.a();
      e.b();
    }

}

Это требует, чтобы классы расширили C, если их значения нужно использовать здесь в качестве параметров. Тип не является подтипом интерфейса, просто реализуя правильные методы; он должен быть явно отмечен.

Jay Conrod 13.01.2009 06:47

Вы правы в том, что для этого требуются классы для расширения C, что также означает, что они расширили A и B. У меня создалось впечатление, что это должен был быть класс SomeClass, у которого был метод, который требовал типа переменной другого класса, который реализовал оба A и B. Итак, я создал C для этого контракта.

scottm 13.01.2009 07:49

Ну, есть нотация <T extends A & B> f(T ab), но вы должны отдавать предпочтение композиции, а не наследованию. Вам действительно не нужно ничего расширять. Просто создайте тип, который является совместный союз (продукт) обоих типов A и B, как показано ниже:

public abstract class P2<A, B> {
  public A _1();
  public B _2();
}

Иногда называется товаром-2 или «парным» типом. Вы можете создать удобный конструктор для них:

public final class P {
  private P() {}
  public static <A, B> P2 p(final A a, final B b) {
    return new P2<A, B>() {
      public A _1() {
        return a;
      }
      public B _2() {
        return b;
      }
    }
  }
}

Обратите внимание, что вы можете использовать один и тот же объект для обоих аргументов, если A и B являются интерфейсами и ваш объект реализует их оба:

P2<A, B> both = P.p(o, o);

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

Вы найдете этот тип, а также продукты до 8 типов, включенных в библиотеку Функциональная Java. Также существует тип под названием Either<A, B>, который представляет собой несвязный союз (сумма) двух типов, так что он содержит значение либо типа A, либо B (или обоих).

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

public void foo(A a, B b) {
    ....
}

Поверьте, это не так уж и сложно.

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