Тип возврата метода для выполнения нескольких интерфейсов - PullRequest
18 голосов
/ 20 января 2011

Можно ли указать метод, который возвращает объект, который реализует два или несколько интерфейсов?

Скажем, у нас есть следующие интерфейсы:

interface FooBar {
    [Foo] & [Bar] getFooBar();
}

interface Foo {
    void doFoo();
}

inteface Bar {
    void doBar();
}

Разработчикам FooBar необходимопредоставить метод getFooBar(), который возвращает экземпляр типа, который заполняет Foo, а также Bar.

. До сих пор я пытался сделать это с обобщениями:

interface FooBar {
    <T extends Foo & Bar> T getFooBar()
}

class SomeImplementor implements FooBar {
    private FooAndBarImpl fSomeField;

    public <T extends Foo & Bar> T getFooBar() {
        return fSomeField;
    }

}

Учитывая, что FooAndBarImpl - это некоторый тип, предоставляемый фреймворком или библиотекой и реализующий Foo и Bar, я думаю, это должно работать.Однако это не так, потому что «FooAndBarImpl нельзя преобразовать в T».Это почему?Контракт, подразумеваемый getFooBar(), на мой взгляд, не нарушен.

Другое решение - определить новый интерфейс, расширяющий Foo и Bar, и использовать его в качестве возвращаемого типа.Я просто не вижу большого смысла возвращать пустую оболочку для fSomeField в реализации getFooBar().

EDIT:

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

Ответы [ 4 ]

14 голосов
/ 20 января 2011

Вы можете сделать T параметром класса:

class SomeImplementor<T extends Foo & Bar> implements FooBar {
    private T fSomeField;

    public T getFooBar() {
        return fSomeField;
    }

}

Что касается того, почему ваш универсальный подход не сработал. Давайте создадим следующие два класса, которые реализуют Foo и Bar:

class A implements Bar, Foo{
   private int a;
   ...
}
class B implements Bar, Foo{
   private String b;
   ...
}
class SomeImplementor implements FooBar {
   private A someField;
   public <T extends Foo & Bar> T getFooBar() {
      return someField;
   }
}

Итак, теперь мы должны выполнить следующее:

SomeImplementor s = new SomeImplementor();
A a = s.getFooBar();
B b = s.getFooBar();

Хотя getFooBar() возвращает объект типа A, у которого нет действительного приведения к типу B (откуда придет член String?), Даже если B удовлетворяет требованию <T extends Foo & Bar>, то есть является действительным T.

Короче говоря, компилятор (помните, что generics - это механизм времени компиляции ) не может гарантировать, что каждый T типа <T extends Foo & Bar> может иметь присвоение ему типа A , Какую именно ошибку вы видите - компилятор не может преобразовать данный A в каждый допустимый T.

2 голосов
/ 20 января 2011
interface FooBar extends Foo, Bar {
    FooBar getFooBar();
}
2 голосов
/ 20 января 2011

Другим решением было бы определить новый интерфейс, расширяющий Foo и Bar, и использовать его в качестве типа возвращаемого значения.

Я бы сказал, пойти для этой опции,

0 голосов
/ 20 января 2011

Вы можете вернуть контейнер для предоставления Foo и Bar.

public class Container{
   private FooBarBam foobar;
   public Bar asBar(){
      return foobar;
   }
   public Foo asFoo(){
      return foobar;
   }
}

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

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

Ответ Дэвина выглядит хорошо, но также требует публичного класса / интерфейса, который реализует Foo и Bar для работы.

Обновление:

Проблема в том, что компилятор не знает, что тип T должен быть FooAndBarImpl, он должен был бы угадывать, а компилятор с угадыванием приводит к плохому и непредсказуемому коду.

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

//Does not compile expecting > after Foo
List<? extends Foo & Bar> getFooBar(){
    final List<FooAndBarImpl> l = new ArrayList<FooAndBarImpl>();
    l.add(new FooAndBarImpl());
    return l;
} 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...