Java 泛型:非法前向引用

发布于 2024-10-10 21:28:49 字数 518 浏览 5 评论 0原文

给定一个通用接口,

interface Foo<A, B> { }

我想编写一个实现,要求 A 成为 B 的子类。所以我想做

class Bar<A, B super A> implements Foo<A, B> { }
// --> Syntax error

or

class Bar<A extends B, B> implements Foo<A, B> { }
// --> illegal forward reference

但似乎唯一有效的解决方案是:

class Bar<B, A extends B> implements Foo<A, B> { }

这有点难看,因为它颠倒了通用参数。
对于这个问题有什么解决方案或解决方法吗?

Given a generic interface

interface Foo<A, B> { }

I want to write an implementation that requires A to be a subclass of B. So I want to do

class Bar<A, B super A> implements Foo<A, B> { }
// --> Syntax error

or

class Bar<A extends B, B> implements Foo<A, B> { }
// --> illegal forward reference

But the only solution that seems to work is this:

class Bar<B, A extends B> implements Foo<A, B> { }

which is kind of ugly, because it reverses the order of the generic parameters.
Are there any solutions or workarounds to this problem?

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

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

发布评论

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

评论(3

暖伴 2024-10-17 21:28:49

由于这在 Java 中是不可能的,请尝试以不同的方式考虑 Bar

当您为 Bar,您首先指定父类,然后指定子类。这就是 Bar 的工作原理。不要认为它是倒退的,而是认为它是前进的。自然应该在子级之前指定父级。您添加的这种附加关系是驱动参数顺序的因素,而不是底层接口。

Since this isn't possible in Java, try to think of Bar<B, A extends B> differently.

When you declare a variable for Bar, you're specifying the parent class first and then the child class. That's how Bar works. Don't think of it as being backwards - think of it as being forwards. The parent should naturally be specified before the child. This additional relationship you added is what drives the parameter order, not the underlying interface.

2024-10-17 21:28:49

看到这个问题后,我花了一点时间尝试了一些我认为可能有效的不同技术。例如,构建通用接口ISuper,然后使用Bar实现 ISuper (以及具有子类和扩展而不是实现的类似技术),但这只会导致类型错误, Bar.java:1: type argument A is not在其范围内。同样,我尝试创建一个方法 private ;条 foo() { 返回这个; }; 并从构造函数中调用它,但这只会导致有趣的类型错误消息 Bar.java:2: 不兼容的类型
发现:酒吧

required: Bar

所以,不幸的是,我认为答案是否定的。显然,这不是您所希望的答案,但似乎正确的答案是这是不可能的。

After seeing this question, I spent a little bit trying some different techniques that I thought might work. For example, building a generic interface ISuper<B,A extends B> and then having Bar<A,B> implements ISuper<B,A> (and a similar technique with a sub-class and extends rather than implements) but this just results in a type error, Bar.java:1: type parameter A is not within its bound. Likewise, I tried creating a method private <A extends B> Bar<A,B> foo() { return this; }; and calling it from the constructor, but this just results in the fun type error message Bar.java:2: incompatible types
found : Bar<A,B>
required: Bar<A,B>

So, I think that, unfortunately, the answer is no. Obviously, it's not the answer you were hoping for, but it seems that the correct answer is that this just isn't possible.

吃颗糖壮壮胆 2024-10-17 21:28:49

已经指出,既没有解决方案也没有好的解决方法。这就是我最终所做的。它仅适用于我的特殊情况,但如果您遇到类似问题,可以将其作为启发。 (这也解释了为什么我遇到这个问题)

首先,有这个类(仅显示相关接口):

class Pipe<Input, Output> {

    boolean hasNext();

    Input getNext();

    void setNext(Output o);

}

Foo 接口实际上是

interface Processor<Input, Output> {

    process(Pipe<Input, Output> p);

}

Bar应该像这样工作

class JustCopyIt<Input, Output> implements Processor<Input, Output> {

    process(Pipe<Input, Output> p) {
       while (p.hasNext()) p.setNext(p.getNext());
    }

}

最简单的方法是像这样转换值:p.setNext((Output) p.getNext())
但这很糟糕,因为它允许创建 JustCopyIt的实例。调用该对象会在某个时刻神秘地失败,但不会在实际发生错误的时刻失败。

执行 class JustCopyIt;实现 Processor 在这里也不起作用,因为这样我就无法处理 Pipe

所以我最终所做的就是将接口更改为:

interface Processor<Input, Output> {

    process(Pipe<? extends Input, ? super Output> p);

}

这样,JustCopyIt就能够处理Pipe

虽然这在技术上似乎是唯一有效的解决方案,但它仍然很糟糕,因为它 1)仅适用于这种特殊情况,2)要求我更改接口(这并不总是可能的)以及 3)制作其他代码处理器丑陋。

编辑:
再次阅读基思的回答激发了我另一种解决方案:

public abstract class Bar<A, B> implements Foo<A, B> {

    public static <B, A extends B> Bar<A, B> newInstance() {
        return new BarImpl<B, A>();
    }

    private static class BarImpl<B, A extends B> extends Bar<A, B> {
        // code goes here
    }

}

// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error

It was pointed out already that there is neither a solution nor a nice workaround. Here is what I finally did. It only works for my special case, but you can take it as an inspiration if you run into similar problems. (It also explains why I ran into this problem)

First of all, there is this class (showing only the relevant interface):

class Pipe<Input, Output> {

    boolean hasNext();

    Input getNext();

    void setNext(Output o);

}

The Foo interface is actually

interface Processor<Input, Output> {

    process(Pipe<Input, Output> p);

}

and the class Bar should work like this

class JustCopyIt<Input, Output> implements Processor<Input, Output> {

    process(Pipe<Input, Output> p) {
       while (p.hasNext()) p.setNext(p.getNext());
    }

}

The easiest way would be to cast the values like this: p.setNext((Output) p.getNext()).
But this is bad as it would allow to create an instance of JustCopyIt<Integer, String>. Calling this object would mysteriously fail at some point, but not at the point where the actual error is made.

Doing class JustCopyIt<Type> implements Processor<Type, Type> would also not work here, because then I am not able to process a Pipe<String, Object>.

So what I finally did was to change the interface to this:

interface Processor<Input, Output> {

    process(Pipe<? extends Input, ? super Output> p);

}

This way, a JustCopyIt<List> is able to process a Pipe<ArrayList, Collection>.

While this technically seems to be the only valid solution, it is still bad because it 1) only works for this special case, 2) required me to change the interface (which is not always possible) and 3) made the code of the other processors ugly.

Edit:
Reading Keiths answer again inspired me for another solution:

public abstract class Bar<A, B> implements Foo<A, B> {

    public static <B, A extends B> Bar<A, B> newInstance() {
        return new BarImpl<B, A>();
    }

    private static class BarImpl<B, A extends B> extends Bar<A, B> {
        // code goes here
    }

}

// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文