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

发布于 2024-12-07 04:27:36 字数 1622 浏览 4 评论 0 原文

假设我正在设计类似以下接口的内容:

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

但是,需要注意的是 method1 的返回类型和 method2 的参数与具体实现匹配,而不仅仅是 我的界面。也就是说,如果我有实现 MyInterfaceMyInterfaceImpl,它需要具有以下内容:

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,也可以编译得很好。*这让我思考我需要别的东西。

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

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

Suppose I am designing something like the following interface:

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

However, there is the caveat that the return type for method1 and the parameter for method2 match the concrete implementation and not just MyInterface. That is, if I have MyInterfaceImpl that implements MyInterface, it needs to have the following:

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

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

As written above, method1 won't cause any compile errors, but there is nothing guaranteeing that the return type matches in all implementations. Of course method2 won't even compile because the signature does not match the interface.

One candidate solution is to use self-referential or recursive bounds in generics:

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

This would get me what I want with one exception: other implementations might pass the wrong generic type (nothing forces T to match the concrete type). So potentially someone else could implement the following:

public class NotMyInterfaceImpl implements MyInterface<MyInterfaceImpl>{
  @Override
  public MyInterfaceImpl method1();

  @Override
  public void method2(MyInterfaceImpl mi);
} 

That would compile just fine even though NotMyInterfaceImpl should implement MyInterface<NotMyInterfaceImpl>.* That makes me think I need something else.

*Note that I don't think I'm trying to violate LSP; I'm OK with the return type/parameter being subclasses of NotMyInterfaceImpl.

So I don't know of a clean way to do this. That leads me to believe that I might be focusing too much on implementation details in the interface, but it doesn't seem that way to me. Is there any way to do the type of thing I described, or is this some kind of smell that I'm putting something in an interface that doesn't belong there?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

始终不够爱げ你 2024-12-14 04:27:36

这正是 Comparable 接口所面临的情况(它的compareTo 方法想要采用与调用它的对象类型相同的参数)。那么它有什么作用呢?它被简单地定义为Comparable。这个想法是,实现类“应该”实现 Comparable 并将其自身作为参数(允许它“与”自身进行“比较”);但这并没有强制执行(因为没有办法做到这一点)。

是的,正如您所指出的,这将允许任何类使用任何其他类的参数实现 Comparable:class Foo 实现 Comparable 其中 Foo code> 和 Bar 彼此没有关系。然而,这并不是真正的问题。

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

(我注意到您使用 >Comparable 仅使用 。虽然 > 将排除类型参数未实现 MyInterface 的情况,但不会排除类型参数确实实现的情况MyInterface,但与类不同。那么,如果您采用Comparable的方式来限制它们的使用位置,那有什么意义呢?无论如何都是类型安全的,所以没有必要添加更多限制。)

This is the exact situation faced by the Comparable interface (its compareTo method wants to take an argument the same type as the object it is called on). So what does it do? It's simply defined as Comparable<T>. The idea is that an implementing class "should" implement Comparable with itself as the parameter (allowing it to "compare to" itself); but this is not enforced (since there is no way to do it).

Yes, as you noted, this will allow any class to implement Comparable with a parameter of any other class: class Foo implements Comparable<Bar> where Foo and Bar have no relation to each other. However, this is not really a problem.

All the methods and classes (sorting, maximum, etc.) that require Comparable objects have the following generic type constraint <T extends Comparable<? super T>>. This ensures that objects of type T are comparable with themselves. That way, it is completely type-safe. So the enforcement is not made in the declaration of the Comparable interface, but in the places that use it.

(I notice that you use <T extends MyInterface<T>> while Comparable uses simply <T>. Although <T extends MyInterface<T>> will exclude cases where the type parameter does not implement MyInterface, it will not exclude cases where the type parameter does implement MyInterface, but is different than the class. So what's the point of half-excluding some cases? If you adopt Comparable's way of restricting it where they are used, it's type-safe anyway, so there is no point in adding more restrictions.)

孤寂小茶 2024-12-14 04:27:36

我相信这是不可能的。根本没有办法在泛型框架中引用对象的实现类,据我所知,也没有任何方法可以用纯泛型构造一个能够约束实现类以匹配类型参数的笼子。

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

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

骆驼穿过针眼比 的实例更容易>NotMyInterfaceImpl 来传递该返回类型。因此,尽管麻烦制造者可以编写不符合总体规划的类,但他们无法从工厂返回它们。除非NotMyInterfaceImpl扩展了MyInterfaceImpl;但从某种意义上说,它也将是一个 MyInterfaceImpl,所以也许这会是正确的?

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

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

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

I believe that this cannot be done. There is simply no way to refer to an object's implementation class in the framework of generics, nor, as far as i know, any way to construct a cage out of pure generics which is capable of constraining the implementation class to match a type parameter.

The most useful thing i can suggest is using a self-referential parameter, and then always acquiring instances of implementations from factory methods which look like:

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

It is easier for a camel to pass through the eye of a needle than for an instance of NotMyInterfaceImpl to pass through that return type. So, although troublemakers could write classes which do not conform to your masterplan, they couldn't return them from factories. Unless NotMyInterfaceImpl extended MyInterfaceImpl; but then, in a sense, it would also be a MyInterfaceImpl, so perhaps that would be kosher?

EDIT: A slightly more useful version of that idea is to always pass instances of implementations of the interface around in a suitably restrictive holder, like:

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

If someone gives you a Holder<Q>, then you know that Q must be a version of MyInterface bound to itself, which is what you're after.

提笔书几行 2024-12-14 04:27:36

返回接口的目的是该方法不关心返回对象的实际实现。在您的情况下,您实际上想要强制该类型成为该接口的特定子实现。

要应用上面描述的约束,恕我直言,设计可能应该是基类而不是接口。这允许您控制实现,例如顶级流程,并将低级策略留给子类来实现:

class MyBaseImpl {
    public final void fixedFlow() {
        MyBaseImpl obj = method1();
        obj.method2(this);
    }
    protected abstract MyBaseImpl method1();
    ....
}

必须有其他方法才能使其有趣......;也许您有充分的理由想要这样做...

希望这会有所帮助!

The point of returning the interface is such that the method does not care the actual implementation of the returned object. In your case you actually want to mandate the type to be a particular sub-implementation of that interface.

To apply the constraints that you described above, IMHO the design should probably be a base class instead of an interface. This allows you to control the implementation, for example a top-level flow, and leave low-level strategy to sub-classes to implement:

class MyBaseImpl {
    public final void fixedFlow() {
        MyBaseImpl obj = method1();
        obj.method2(this);
    }
    protected abstract MyBaseImpl method1();
    ....
}

There has to be other methods to make it interesting...; perhaps you have good reasons to want to do this...

Hope this helps!

还在原地等你 2024-12-14 04:27:36

您尝试做的事情是不合法的,因为您试图缩小实现类型的参数,而这个“没有意义”。您尝试使用“协变”参数,并且仅允许协变返回类型(甚至逻辑,并且仅从 Java 5 开始支持)。

我的意思是,如果可以使用协变参数类型,您可以执行以下操作:

MyInterface instance = new MyInterfaceImpl();

然后,使用接口支持但 MyInterfaceImpl 类不支持的另一个实现来调用“实例”方法:

instance.method2(new MyInterfaceImpl_2());

Java 无法转换 < code>MyInterfaceImpl_2 到 MyInterfaceImpl,因此它会阻止您在编译时这样做。

您可以做的是使用“逆变”参数来扩大参数,这将是逻辑。有关这方面的更多详细信息,请检查此答案:

在 Java 中演示协变和逆变?

我能想到的唯一解决方法是在运行时解决问题,我的意思是,做这样的事情:

public class MyInterfaceImpl implements MyInterface{
  @Override
  public void method2(MyInterface mi){
        realMethod((MyInterfaceImpl) mi);
  }
  public void realMethod(MyInterfaceImpl) {...}
}

但是当然,你可能会得到 ClassCast 异常。

What you are trying to do is not legal because you are trying to narrow the parameter of the implemented type, and this "does not make sense". You are tryint to use "covariant" parameters, and only covariant return types are allowed (and even logic, and only supported from Java 5).

I mean, if it was possible to use covariant parameter types, you could do things like:

MyInterface instance = new MyInterfaceImpl();

And then, invoke on "instance" the method with another implementation supported by the interface but not supported by the MyInterfaceImpl class this way:

instance.method2(new MyInterfaceImpl_2());

Java cannot convert MyInterfaceImpl_2 to MyInterfaceImpl, so it prevents you from doing so at compilation time.

What you could do is to widen the parameter, using "contravariant" parameter, which would be logic. For more detail on this, check this anser:

Demonstrate covariance and contravariance in Java?

The only workaround that I can think of, is to solve the problem at runtime, I mean, doing something like this:

public class MyInterfaceImpl implements MyInterface{
  @Override
  public void method2(MyInterface mi){
        realMethod((MyInterfaceImpl) mi);
  }
  public void realMethod(MyInterfaceImpl) {...}
}

But you could get ClassCast exception, of course.

风苍溪 2024-12-14 04:27:36

这是您要找的吗?

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,而不是使它们抽象。

Is this what you are looking for?

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

}

And you could even implement method 1 or 2 instead of making them abstract.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文