2

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

Community
  • 1
  • 1
Cydhra
  • 581
  • 1
  • 7
  • 17
  • This is not clear. If this code works, then what is the code which doesn't work for you? – Alma Alma Jun 29 '16 at 23:45
  • Please also post the code where your component renderer is using those listboxes – Alma Alma Jun 29 '16 at 23:46
  • The code does not work. It doesn't compile. There is no code in the renderer using the listboxes yet. It simply doesn't compile. – Cydhra Jun 29 '16 at 23:49
  • Well that's strange. I tried your code and it compiles for me without any warning or error in Java 8. – Alma Alma Jun 29 '16 at 23:52
  • I've tried to compile it with maven: Source and Target 1.8 - same error. My IDE is IntelliJ IDEA, Java Version 1.8_51. IDEA isn't showing a syntax error or warning about this, and I dont see, why the wildcard, that has nothing to do with the bound of T shouldn't work... – Cydhra Jun 29 '16 at 23:55
  • I see. You didn't mention maven earlier. Please update your post that this error is only happening in maven. – Alma Alma Jun 29 '16 at 23:57
  • Also please post your pom xml – Alma Alma Jun 29 '16 at 23:58
  • It does not occur only in maven. I was just saying, that I also tried to compile it in maven, to double check, that it isn't an issue with my IDE. But maven fails with the same error, so it is clearly a java issue. My pom, I will include anyways – Cydhra Jun 30 '16 at 00:00
  • @CaptainFogetti It doesn't compile. Please see the following: http://ideone.com/OZQ1UG. – Radiodef Jun 30 '16 at 00:04
  • @Radiodef Yes, that is the exact problem. – Cydhra Jun 30 '16 at 00:05
  • Unfortunately I don't really have a good idea for a solution right now. The wildcard doesn't work well with the recursive type bound. One way is to be less restrictive like `interface ComponentRenderer> {}` but I don't know if that fits your needs. Depends on why you are using the recursive type bound. The technique has limitations. – Radiodef Jun 30 '16 at 00:09
  • Yeah, I had problems before, wich lead to the recursive type bound, as I wrote on top of my question. I need the bound to restrict the renderer on different GuiComponents. To use it for other objects is not intended and therefore not a good code style. – Cydhra Jun 30 '16 at 00:12
  • @Radiodef How come that this is compiling in IntelliJ IDEA and Eclipse, and failing in other environments? – Alma Alma Jun 30 '16 at 00:16
  • @CaptainFogetti it doesn't compile in IntelliJ and probably also not in eclipse, if you followed the instructions leading to the error properly. It ONLY compiles, when you use a generic type for the renderer, wich I do not want, since it is pointless and restricts my further implementations. – Cydhra Jun 30 '16 at 00:18
  • @Cydhra I can reproduce the problem in Maven! But not in Eclipse. And you also told me earlier that " IDEA isn't showing a syntax error or warning about this, and I dont see, why the wildcard, that has nothing to do with the bound of T shouldn't work". So still my question is to Radiodef why is this compiling in Eclipse but not in Maven? – Alma Alma Jun 30 '16 at 00:25
  • Yeah, IDEA does not show an error, it does not mark the line of code at all, but it cannot compile it either. That eclipse is able to do this, while maven can't, confuses me. – Cydhra Jun 30 '16 at 00:27
  • @Cydhra Ok, I got you! Hope we can sort this out. :) – Alma Alma Jun 30 '16 at 00:28
  • @Cydhra OK, in eclipse case the problem is a bug already reported: https://bugs.eclipse.org/bugs/show_bug.cgi?id=456459 – Alma Alma Jun 30 '16 at 00:38
  • Eclipse has its own compiler which behaves differently sometimes. Actually I'm not entirely sure which one is correct. I kind of think that it should compile. Type inference is fine with it: http://ideone.com/0oznSe. – Radiodef Jun 30 '16 at 01:37

1 Answers1

1

OK, I found it I think. So both eclipse and intellij idea got the generics wrong. (Which is not a surprise IMHO since it's very easy to get confused)

So there is/was a bug intellij idea which prevents it from showing any error: Java code that does not compile with JDK 8, but IntelliJ does not report an error

And there is/was a similar error in eclipse: Discrepancy between Eclipse compiler and javac - Enums, interfaces, and generics

I hope it will help those people who will scratch their head in the future.

Alma Alma
  • 1,641
  • 2
  • 16
  • 19
  • Yeah, that is exactly the bug. But does anyone know *why* this bug occures, though the code would make sense and can anyone give a hint, how to fix, bypass or workaround this? – Cydhra Jun 30 '16 at 08:56
  • Ok, since I researched a lottle on you links and discussions on them, it seems, that this construct, I was searching once were able to compile, but differs from specification in some point, so it was fixed and forbidden. I personally do not understand why this was done, because I don't see, where there's a problem and for me, this isn't a theoretically construct but I do want to implement and use those features, which are now disabled. Sad story... – Cydhra Jun 30 '16 at 09:02
  • @Cydhra Well this happens sometimes. At least you know what caused the strange behavior. If you think this answered your question "Why does this error occure, though my IDE (IntelliJ IDEA) does not mark this, but fails building?" then please mark it accepted. Happy coding! – Alma Alma Jun 30 '16 at 12:56