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