接口如何包含在其签名或返回类型中引用接口的具体实现类型的方法?
假设我正在设计类似以下界面:
public interface MyInterface{ public MyInterface method1(); public void method2(MyInterface mi); }
但是,需要注意的是, method1
的返回类型和method2
的参数与具体实现相匹配,而不仅仅是MyInterface
。 也就是说,如果我有实现MyInterface
,它需要具有以下内容:
public class MyInterfaceImpl implements MyInterface{ @Override public MyInterfaceImpl method1(){...} @Override public void method2(MyInterfaceImpl mi){...} }
如上所述, method1
不会导致任何编译错误,但没有任何保证返回类型在所有实现中都匹配。 当然, method2
甚至不会编译,因为签名与接口不匹配。
一个候选解决方案是在generics中使用自引用或递归边界:
public interface MyInterface<T extends MyInterface>{ public T method1(); public void method2(T mi); } public class MyInterfaceImpl implements MyInterface{ @Override public MyInterfaceImpl method1(); @Override public void method2(MyInterfaceImpl mi); }
这将得到我想要的一个例外:其他实现可能会传递错误的generics类型(没有强制T
匹配具体类型)。 所以其他人可能会实现以下内容:
public class NotMyInterfaceImpl implements MyInterface{ @Override public MyInterfaceImpl method1(); @Override public void method2(MyInterfaceImpl mi); }
即使NotMyInterfaceImpl
应该实现MyInterface
这也会很好地编译。*这让我觉得我需要别的东西。
*请注意,我不认为我试图违反LSP; 我很好,返回类型/参数是NotMyInterfaceImpl
子类。
所以我不知道干净的方法。 这让我相信我可能过于关注界面中的实现细节,但对我来说似乎并不是这样。 有没有办法做我描述的那种东西,或者这是某种气味,我把东西放在一个不属于那里的界面?
这是Comparable
接口所面临的确切情况(其compareTo
方法希望采用与调用它的对象相同的类型)。 那它是做什么用的? 它简单地定义为Comparable
。 这个想法是一个实现类“应该”实现Comparable
自身作为参数(允许它“自我比较”); 但这没有强制执行(因为没有办法)。
是的,正如您所指出的,这将允许任何类使用任何其他类的参数class Foo implements Comparable
: class Foo implements Comparable
,其中Foo
和Bar
彼此无关。 但是,这不是一个真正的问题。
需要Comparable
对象的所有方法和类(排序,最大值等)都具有以下generics类型约束
。 这确保了类型T的对象与它们自身相当。 这样,它完全是类型安全的。 因此,强制执行不是在Comparable接口的声明中,而是在使用它的地方。
(我注意到你使用
而Comparable
使用
。虽然
将排除类型参数未实现MyInterface
,但它不会排除类型参数确实实现了MyInterface
,但是与类不同。那么半数排除某些情况的重点是什么呢?如果你采用Comparable
的方式将它限制在使用的地方,那么无论如何都是类型安全的,所以没有意义添加更多限制。)
我相信这不可能做到。 在generics框架中根本没有办法引用对象的实现类,据我所知,也没有办法用纯generics构造一个笼子,它能够约束实现类以匹配一个类型参数。
我可以建议最有用的是使用自引用参数,然后总是从工厂方法中获取实现的实例,如下所示:
public > T newInstance();
骆驼穿过针眼比穿过返回类型的NotMyInterfaceImpl
实例更容易。 因此,虽然麻烦制造者可以编写不符合您的总体规划的课程,但他们无法从工厂返回。 除非NotMyInterfaceImpl
扩展MyInterfaceImpl
; 但是,从某种意义上说,它也是一个MyInterfaceImpl
,所以也许那将是犹太人?
编辑:这个想法的一个稍微有用的版本是总是在适当限制的持有者中传递接口的实现的实例,例如:
class Holder> { public final T value; }
如果有人给你一个Holder
,那么你就知道Q
必须是MyInterface
一个版本,这就是你所追求的。
返回接口的意义在于该方法不关心返回对象的实际实现。 在您的情况下,您实际上希望将类型强制为该接口的特定子实现。
要应用上面描述的约束,恕我直言,设计应该是基类而不是接口。 这允许您控制实现,例如顶级流,并将低级策略留给子类来实现:
class MyBaseImpl { public final void fixedFlow() { MyBaseImpl obj = method1(); obj.method2(this); } protected abstract MyBaseImpl method1(); .... }
必须有其他方法让它变得有趣……; 也许你有充分的理由想要这样做……
希望这可以帮助!
你试图做的是不合法的,因为你试图缩小已实现类型的参数,这“没有意义” 。 您是tryint使用“covariant”参数 ,并且只允许协变返回类型(甚至逻辑,并且仅支持Java 5 )。
我的意思是,如果可以使用协变参数类型,您可以执行以下操作:
MyInterface instance = new MyInterfaceImpl();
然后,使用接口支持的另一个实现调用“instance”方法,但MyInterfaceImpl类不支持这种方式:
instance.method2(new MyInterfaceImpl_2());
Java无法将MyInterfaceImpl_2
转换为MyInterfaceImpl
,因此它会阻止您在编译时这样做。
你可以做的是使用“contravariant”参数扩展参数,这将是逻辑。 有关详细信息,请查看此anser:
展示Java中的协方差和逆变?
我能想到的唯一解决方法是在运行时解决问题,我的意思是,做这样的事情:
public class MyInterfaceImpl implements MyInterface{ @Override public void method2(MyInterface mi){ realMethod((MyInterfaceImpl) mi); } public void realMethod(MyInterfaceImpl) {...} }
但是你当然可以获得ClassCastexception。
这是你想要的?
public interface MyInterface { static abstract class MyInterfaceImpl implements MyInterface { @Override public abstract MyInterfaceImpl method1(); @Override public abstract void method2(MyInterfaceImpl mi); } MyInterfaceImpl method1(); void method2(MyInterfaceImpl mi); }
你甚至可以实现方法1或2,而不是将它们抽象化。