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




Вы можете сделать это с включенными дженериками. Например, чтобы принять экземпляр некоторого класса, который реализует как CharSequence, так и Appendable:
public <T extends CharSequence & Appendable> void someMethod(T param) {
...
}
В зависимости от дизайна вы можете выполнить одно из следующих действий:
В любом случае вы захотите иметь контракт, который включает как a (), так и b ().
Спасибо, но вариант наследования не всегда имеет смысл (A и B могут быть совершенно не связаны. Мне тоже не очень нравится второй вариант, потому что это означает, что 1) мне, возможно, придется сделать `` интерфейсы C '' для всех возможных комбинация интерфейсов и 2) классы должны все это реализовывать ...
Как я уже сказал, это зависит от дизайна. Если бы A и B были связаны, то 1) работал бы. 2) На самом деле грешить не так уж и плохо. A и B несколько связаны, потому что вы передаете объект, который реализует оба. А как насчет базового класса, который реализует и то, и другое?
Просто снимаю в темноте, я не знаю, правильный ли это синтаксис, т.е. если вам нужно повторно объявить методы в 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, если их значения нужно использовать здесь в качестве параметров. Тип не является подтипом интерфейса, просто реализуя правильные методы; он должен быть явно отмечен.
Вы правы в том, что для этого требуются классы для расширения C, что также означает, что они расширили A и B. У меня создалось впечатление, что это должен был быть класс SomeClass, у которого был метод, который требовал типа переменной другого класса, который реализовал оба A и B. Итак, я создал C для этого контракта.
Ну, есть нотация <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) {
....
}
Поверьте, это не так уж и сложно.
Это кажется запутанным, поскольку вам все еще нужно определить тип T, который реализует как A, так и B. Почему бы просто не сделать его типом paramater? Если многие объекты реализуют A и B, почему бы не создать интерфейс C: A, B?