【发布时间】:2016-06-29 23:05:56
【问题描述】:
我有一个通用 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 方法中,列表元素只应被视为对象并调用toString()。但是这个实现不起作用:
Error:(21, 73) java: type argument [...]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 构建完全相同的错误。澄清:代码有效,如果我向渲染器添加一个类型,但这对我不起作用,因为渲染器应添加到所有列表中,尽管列表的类型。这就是我想使用通配符的原因。
编辑#2:我改变了我的描述的语法流程,并添加了一个代码sn-p来清除错误发生的时间,以及代码编译的时间。
编辑#3:由于 cmets 和第一个答案试图重现并研究我的“错误”,所以到目前为止的结果如下:
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<?>>
没有引发编译器错误,尽管错误报告者提到了 JLS (Java Language Specification) 的某些部分,这些部分违反了这些结构。规范中提到的部分是处理 Bbund 交集,所以泛型具有多个边界:
class D<T extends T1 & T2 & T3...>
(如果我理解正确的话)。掌握 Java 泛型的人 Maurizius 回答说,确实,当时的规范不清楚,因此构造导致编译时错误。 我个人不明白这一点,因为我在这里看不到任何违反类型边界的行为,但我辞职并让我的 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 泛型不是这种语言的一个很好的特性,许多其他语言提供更好的泛型,它们是运行时特性不仅仅是代码风格的功能。但那是另一回事了)
我的 FlatListBoxRender DOES 确实有它的类型参数在界限内,因为类型是 GuiListBox,在我的理解中扩展了 GuiComponent(在这个地方也不对,因为 GuiComponent 确实是有界的,但是这个界限是由 GuiListBox 的声明来完成的):
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中找到渲染器的实例
【问题讨论】:
-
这个不清楚。如果此代码有效,那么哪些代码不适合您?
-
请同时发布您的组件渲染器使用这些列表框的代码
-
代码不起作用。它不编译。渲染器中还没有使用列表框的代码。它根本无法编译。
-
这很奇怪。我尝试了您的代码,它为我编译,在 Java 8 中没有任何警告或错误。
-
我尝试用 maven 编译它:Source and Target 1.8 - 同样的错误。我的 IDE 是 IntelliJ IDEA,Java 版本 1.8_51。 IDEA 没有显示语法错误或警告,我不明白为什么与 T 的界限无关的通配符不应该工作......