Предположим, что я создаю что-то вроде следующего интерфейса:
public interface MyInterface{
public MyInterface method1();
public void method2(MyInterface mi);
}
Однако существует предостережение о том, что возвращаемый тип для method1
и параметр method2
соответствуют конкретной реализации, а не только MyInterface
. То есть, если у меня есть MyInterfaceImpl
, который реализует MyInterface
, он должен иметь следующее:
public class MyInterfaceImpl implements MyInterface{
@Override
public MyInterfaceImpl method1(){...}
@Override
public void method2(MyInterfaceImpl mi){...}
}
Как указано выше, method1
не приведет к ошибкам компиляции, но нет ничего, гарантирующего, что возвращаемый тип совпадает во всех реализациях. Конечно, method2
даже не компилируется, потому что подпись не соответствует интерфейсу.
Одним из возможных решений является использование самореферентных или рекурсивных границ в дженериках:
public interface MyInterface<T extends MyInterface<T>>{
public T method1();
public void method2(T mi);
}
public class MyInterfaceImpl implements MyInterface<MyInterfaceImpl>{
@Override
public MyInterfaceImpl method1();
@Override
public void method2(MyInterfaceImpl mi);
}
Это обеспечит мне то, что я хочу, с одним исключением: другие реализации могут передавать неправильный общий тип (ничто не заставляет T
соответствовать конкретному типу). Поэтому потенциально кто-то другой может реализовать следующее:
public class NotMyInterfaceImpl implements MyInterface<MyInterfaceImpl>{
@Override
public MyInterfaceImpl method1();
@Override
public void method2(MyInterfaceImpl mi);
}
Это скомпилировалось бы просто отлично, хотя NotMyInterfaceImpl
должен реализовывать MyInterface<NotMyInterfaceImpl>
. * Это заставляет меня думать, что мне нужно что-то еще.
* Заметьте, что я не думаю, что пытаюсь нарушить LSP; Я в порядке с возвращаемым типом/параметром, являющимся подклассами NotMyInterfaceImpl
.
Поэтому я не знаю, как это сделать. Это заставляет меня думать, что я могу сосредоточиться на деталях реализации интерфейса, но для меня это не похоже. Есть ли способ сделать то, что я описал, или это какой-то запах, который я вкладываю в интерфейс, который там не принадлежит?