I got a Generic GuiComponent class:
public abstract class GuiComponent<THIS extends GuiComponent<THIS>> extends Gui {
//...
}
(If you ask youself, what this type parameter is doing, look at Calling a Generic Interface Method does not work)
This class got a bunch of component subclasses like this:
public class ConcreteComponent extends GuiComponent<ConcreteComponent> {
//...
}
They all got renderers:
public interface ComponentRenderer<T extends GuiComponent<T>> {
//...
}
wich have concrete implementations:
public class FlatConcreteComponentRenderer implements ComponentRenderer<ConcreteComponent> {
//...
}
But now I got the following class:
public class GuiListBox<U> extends GuiComponent<GuiListBox<U>> {
//...
}
which is generic itself. This leads to the following Renderer Implementation:
public class FlatListBoxRenderer implements ComponentRenderer<GuiListBox<?>> {
//...
}
Because the renderer do not need the type of the listbox AND shall be used for ALL types of listboxes, I use the wildcard, so I do not have to care about the type. Inside the draw method of the renderer, the list elements just shall be treated as objects and toString()
is called. But this implementation does not work:
Error:(21, 73) java: type argument [...]components.GuiListBox is not within bounds of type-variable T
I need to add a type to the renderer, just to use it for GuiListBox
, then it compiles:
public class FlatListBoxRenderer<T> implements ComponentRenderer<GuiListBox<T>>
But this is not very useful, because the same renderer instance shall be handled to all ListBoxes by default. Why does this error occure, though my IDE (IntelliJ IDEA) does not mark this, but fails building?
EDIT #1: I use maven to compile the project, but neither my IDE nor maven are able to compile the class. Anyways, here is my 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 outputs the exact same error as my IDE build. To clarify: The Code works, if I add a type to the Renderer, but this wont work for me, because the renderer shall be added to all List, despite the type of list. That is why I want to use the wildcard.
EDIT #2: I changed the grammar flow of my description and added a code snippet to clearify when the error occurs, and when the code compiles.
EDIT #3: Since the comments and first answer tried to reproduce and do research on my "bug", here are the results so far:
http://mail.openjdk.java.net/pipermail/compiler-dev/2015-June/009604.html
Someone was reporting a bug in Java Compiler, where the following Statement, which is basically my problematic statement:
class C1<T extends C1<T>> {}
class C2<U> extends C1<C2<U>> {}
C1<? extends C2<String>>
C1<? extends C2<?>>
Did NOT throw a compiler error, though the bug reporter was mentioning some parts of the JLS (Java Language Specification), which are violated of those constructions. The mentioned parts of the specification were dealing with Bbund intersections, so generics with multiple bounds:
class D<T extends T1 & T2 & T3...>
(if I understood that correctly). Maurizius, the guy who has his hands on Java generics, replied, that indeed, the specification where unclear at that point and so the construct was made to result in a compile time error. I personally do not understand this, because I cannot see any violation of the type bounds here, but I resigned and made my ListBoxRenderer look like this:
public class FlatListBoxRenderer implements ComponentRenderer<GuiListBox<Object>> {
public void draw(final GuiListBox<Object> component) {
//...
}
}
I thought, since the renderer doesn't care about the list content at all, I could just give a unspecific type argument and use the Renderer for any purpose later. But now I come to the following error:
I got a method somewhere else in my project:
public <T extends GuiComponent<T>> void setComponentRenderer(final Class<T> componentClass,
final ComponentRenderer<T> renderer)
this method assignes a Renderer to a class of GuiElements, so all instances of the GuiElements get a renderer by default. The method call
this.setComponentRenderer(GuiListBox.class, new FlatListBoxRenderer());
fails, because:
Inferred Type java.lang.Object for type parameter T is not within bounds; should extend [...]GuiComponent<java.lang.Object>
This error message does not make sense to me either, and slowly I get really confused (I btw know, that Java Generics aren't a nice feature of this language and many other languages offer far better Generics, which are runtime features and not only code style features. But that's another story)
My FlatListBoxRender DOES indeed have its type parameter within bounds, because the type is GuiListBox, which in my understanding extends GuiComponent (which isn't right in this place either, because GuiComponent is indeed bounded, but this bound is fullfilled by GuiListBox' declaration):
public class GuiListBox<U> extends GuiComponent<GuiListBox<U>> {}
I know my constructions are very complex, but Java isn't designed to just accept ArrayList, it should handle more than one Generic as well.
So, if anyone got solutions to my problem, please let me know. In Addition: Here is my full project code by the way: https://github.com/Cydhra/Util/tree/master/src/main/java/de/cydhra/customgui
You can find all the GuiComponents in the package components
, all the renderer implementations in renderer.defaults
, and the instances of the renderers in renderer.theme