接口如何包含在其签名或返回类型中引用接口的具体实现类型的方法?

假设我正在设计类似于以下接口的东西:

public interface MyInterface{
  public MyInterface method1();
  public void method2(MyInterface mi);
}

但是,需要注意的是,的返回类型和 的参数与具体实现匹配,而不仅仅是 。也就是说,如果我有实现,它需要具有以下特性:method1method2MyInterfaceMyInterfaceImplMyInterface

public class MyInterfaceImpl implements MyInterface{
  @Override
  public MyInterfaceImpl method1(){...}

  @Override
  public void method2(MyInterfaceImpl mi){...}
}

如上所述,不会导致任何编译错误,但不能保证返回类型在所有实现中都匹配。当然,甚至不会编译,因为签名与接口不匹配。method1method2

一个候选解决方案是在泛型中使用自引用或递归边界:

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);
} 

即使应该实现.*,那也可以编译得很好。*这让我觉得我需要别的东西。NotMyInterfaceImplMyInterface<NotMyInterfaceImpl>

*请注意,我不认为我试图违反LSP;我同意返回类型/参数是 的子类。NotMyInterfaceImpl

所以我不知道有一个干净的方法来做到这一点。这让我相信我可能过于关注界面中的实现细节,但对我来说似乎不是这样。有没有办法做我描述的那种事情,或者这是某种气味,我把一些东西放在一个不属于那里的界面中?


答案 1

这是接口面临的确切情况(其方法希望采用与调用它的对象类型相同的参数)。那么它有什么作用呢?它被简单地定义为.这个想法是,一个实现类“应该”实现,以自身作为参数(允许它“比较”自身);但这不是强制执行的(因为没有办法做到这一点)。ComparablecompareToComparable<T>Comparable

是的,如前所述,这将允许任何类使用任何其他类的参数实现:其中 和 彼此没有关系。但是,这并不是一个真正的问题。Comparableclass Foo implements Comparable<Bar>FooBar

所有需要对象的方法和类(排序、最大值等)都具有以下泛型类型约束 。这可确保 T 类型的对象与它们本身具有可比性。这样,它是完全类型的安全。因此,强制不是在可比较接口的声明中进行的,而是在使用它的地方进行的。Comparable<T extends Comparable<? super T>>

(我注意到你使用而使用而只是使用。虽然会排除类型参数不实现的情况,但不会排除类型参数实现,但与类不同的情况。那么,半排除某些情况有什么意义呢?如果你采用 的方式来限制它在哪里使用它们,那么它无论如何都是类型安全的,所以添加更多限制是没有意义的。<T extends MyInterface<T>>Comparable<T><T extends MyInterface<T>>MyInterfaceMyInterfaceComparable


答案 2

我认为,这是做不到的。据我所知,根本没有办法在泛型的框架中引用对象的实现类,也没有办法从纯泛型中构造一个笼子,它能够约束实现类以匹配类型参数。

我能建议的最有用的事情是使用自引用参数,然后总是从工厂方法中获取实现实例,如下所示:

public <T extends MyInterface<T>> T newInstance();

骆驼穿过针眼比通过该返回类型更容易。因此,尽管麻烦制造者可以编写不符合您总体规划的课程,但他们无法从工厂退回它们。除非扩展 ;但是,从某种意义上说,它也会是一个,所以也许那将是犹太洁食?NotMyInterfaceImplNotMyInterfaceImplMyInterfaceImplMyInterfaceImpl

编辑:这个想法的一个更有用的版本是总是在适当的限制性持有者中传递接口的实现实例,例如:

class Holder<T extends MyInterface<T>> {
    public final T value;
}

如果有人给你一个,那么你知道那一定是绑定到自身的一个版本,这就是你所追求的。Holder<Q>QMyInterface