Java通用通配符打破了类型边界

时间:2016-06-29 23:05:56

标签: java generics wildcard

我有一个Generic GuiComponent类:

public abstract class GuiComponent<THIS extends GuiComponent<THIS>> extends Gui {
    //...
}

(如果你问自己,这个类型参数是做什么的,请看Calling a Generic Interface Method does not work

这个类有一堆像这样的组件子类:

public class ConcreteComponent extends GuiComponent<ConcreteComponent> {
    //...
}

他们都有渲染器:

public interface ComponentRenderer<T extends GuiComponent<T>> {
    //...
}

具体实施:

public class FlatConcreteComponentRenderer implements ComponentRenderer<ConcreteComponent> {
    //...
}

但现在我得到了以下课程:

public class GuiListBox<U> extends GuiComponent<GuiListBox<U>> {
    //...
}

这是通用的。这导致以下渲染器实现:

public class FlatListBoxRenderer implements ComponentRenderer<GuiListBox<?>> {
    //...
}

因为渲染器不需要列表框的类型并且将用于所有类型的列表框,所以我使用通配符,所以我不必关心类型。在渲染器的draw方法中,list元素只被视为对象,并且toString()被调用。但是这种实现不起作用:

错误:(21,73)java:type参数[...] components.GuiListBox不在类型变量T的范围内

我需要向渲染器添加一个类型,只是将其用于GuiListBox,然后编译:

public class FlatListBoxRenderer<T> implements ComponentRenderer<GuiListBox<T>>

但这不是很有用,因为默认情况下,所有ListBox都应处理相同的渲染器实例。为什么会出现此错误,尽管我的IDE(IntelliJ IDEA)没有标记这个,但是无法构建?

编辑#1:我使用maven编译项目,但我的IDE和maven都无法编译该类。无论如何,这是我的pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>de.cydhra</groupId>
    <artifactId>Utility</artifactId>
    <version>1.0-SNAPSHOT</version>

    <build>

        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.5.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>

    </build>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.2</version>
        </dependency>
    </dependencies>
</project>

Maven输出与我的IDE构建完全相同的错误。澄清一下:如果我向Renderer添加一个类型,Code会起作用,但这对我不起作用,因为渲染器应该被添加到所有List,尽管列表的类型。这就是我想使用通配符的原因。

编辑#2:我更改了描述的语法流程,并添加了一个代码片段,以便在错误发生时和代码编译时进行清除。

编辑#3:由于评论和第一个答案试图复制并对我的&#34; bug&#34;进行研究,以下是迄今为止的结果:

http://mail.openjdk.java.net/pipermail/compiler-dev/2015-June/009604.html

有人在Java编译器中报告了一个错误,其中包含以下语句,这基本上是我的问题陈述:

class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}

C1<? extends C2<String>>
C1<? extends C2<?>>

没有抛出编译器错误,尽管bug报告者提到了JLS的某些部分(Java Language Specification),这些部分违反了这些结构。规范中提到的部分涉及Bbund交叉点,因此具有多个边界的泛型:

class D<T extends T1 & T2 & T3...>

(如果我理解正确的话)。 Maurizius,那个掌握Java泛型的人,回答说,确实,规范在那一点上不清楚,因此构造导致编译时错误。 我个人不明白这一点,因为我在这里看不到任何违反类型边界的行为,但我辞职并使我的ListBoxRenderer看起来像这样:

public class FlatListBoxRenderer implements ComponentRenderer<GuiListBox<Object>> {

    public void draw(final GuiListBox<Object> component) {
        //...
    }
}

我想,由于渲染器根本不关心列表内容,我可以给出一个非特定类型的参数,并在以后使用渲染器用于任何目的。但是现在我遇到了以下错误:

我在项目的其他地方找到了一个方法:

public <T extends GuiComponent<T>> void setComponentRenderer(final Class<T> componentClass,
                                                                 final ComponentRenderer<T> renderer)

此方法将Renderer分配给一个GuiElements类,因此GuiElements的所有实例默认都会获得一个渲染器。方法调用

this.setComponentRenderer(GuiListBox.class, new FlatListBoxRenderer());

失败,因为:

Inferred Type java.lang.Object for type parameter T is not within bounds; should extend [...]GuiComponent<java.lang.Object>

这个错误信息对我来说也没有意义,慢慢地我真的很困惑(我知道,Java Generics并不是这种语言很好的功能,许多其他语言提供了更好的泛型,它们是运行时功能,而不仅仅是代码风格的功能。但这是另一个故事)

我的FlatListBoxRender DOES 确实在bounds中有它的类型参数,因为类型是GuiListBox,在我的理解中扩展了GuiComponent(在这个地方也不是,因为GuiComponent确实是有界,但这个界限是由GuiListBox&#39;声明完成的:

public class GuiListBox<U> extends GuiComponent<GuiListBox<U>> {}

我知道我的构造非常复杂,但Java并不是为了接受ArrayList而设计的,它也应该处理多个Generic。

所以,如果有人能解决我的问题,请告诉我。 另外:顺便说一下,这是我的完整项目代码: https://github.com/Cydhra/Util/tree/master/src/main/java/de/cydhra/customgui

您可以在包components中找到所有GuiComponents,在renderer.defaults中找到所有渲染器实现,在renderer.theme中找到渲染器的实例

1 个答案:

答案 0 :(得分:1)

好吧,我觉得我发现了。因此,eclipse和intellij的想法都使得泛型错误。 (这不是一个惊喜恕我直言,因为它很容易混淆)

因此有一个错误的intellij想法阻止它显示任何错误: Java code that does not compile with JDK 8, but IntelliJ does not report an error

在eclipse中有/类似的错误: Discrepancy between Eclipse compiler and javac - Enums, interfaces, and generics

我希望它能够帮助那些将来会刮伤他们的人。

相关问题