简体   繁体   English

Java泛型:非法的前向引用

[英]Java generics: Illegal forward reference

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 我想编写一个实现,要求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

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? 这个问题有什么解决方案或解决方法吗?

Since this isn't possible in Java, try to think of Bar<B, A extends B> differently. 由于这在Java中是不可能的,因此尝试以不同的方式考虑Bar<B, A extends B>

When you declare a variable for Bar , you're specifying the parent class first and then the child class. Bar声明变量时,首先指定父类,然后指定子类。 That's how Bar works. 这就是Bar工作方式。 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. 您添加的这个附加关系是驱动参数顺序的因素,而不是底层接口。

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 . 例如,构建一个通用接口ISuper<B,A extends B>然后让Bar<A,B> implements ISuper<B,A> (和一个类似的技术与子类和扩展而不是实现)但这只是导致类型错误, 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; }; 同样,我尝试创建一个方法private <A extends B> Bar<A,B> foo() { return this; }; 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> 并从构造函数中调用它,但这只会导致有趣的类型错误消息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. 显然,这不是你所希望的答案,但似乎正确的答案是,这是不可能的。

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 实际上是Foo接口

interface Processor<Input, Output> {

    process(Pipe<Input, Output> p);

}

and the class Bar should work like this Bar类应该像这样工作

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()) . 最简单的方法是抛出这样的值: p.setNext((Output) p.getNext()) But this is bad as it would allow to create an instance of JustCopyIt<Integer, String> . 但这很糟糕,因为它允许创建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> . class JustCopyIt<Type> implements Processor<Type, Type>在这里也行不通,因为那时我无法处理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> . 这样, JustCopyIt<List>就能够处理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. 虽然这在技术上似乎是唯一有效的解决方案,但它仍然很糟糕,因为它1)仅适用于这种特殊情况,2)要求我更改接口(这并不总是可行)和3)制作另一个的代码处理器丑陋。

Edit: 编辑:
Reading Keiths answer again inspired me for another solution: 阅读Keiths的回答再次启发了我的另一个解决方案:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM