Compiler bug or not?
Note, this is a cross-post. The original question was posted in the java programming forum:
http://forum.java.sun.com/thread.jspa?threadID=5172288
I have however "closed" the other thread, so this is the main thread.
-
Hi all,
I have just filed a bug report on the compiler (both 1.5 and 1.6 on windows). I haven't tested it on other platforms. I'm not 100% sure that it's a bug, the problem can be that I don't fully understand how generics should work in this case. The code below compiles fine in Eclipse, but javac barfs.
interface Request{
}
interface Response{
}
class XmlRequestimplements Request{
}
class XmlResponseimplements Response{
}
class XmlRequestHandlerextends RequestHandler<XmlRequest, XmlResponse>{
//javac gives:
//type parameter XmlRequestHandler.RequestExecutor is not within its bound
class Taskextends AbstractTask<RequestExecutor>{
}
class RequestExecutorextends AbstractRequestExecutor{
publicvoid run(){
}
}
}
class RequestHandler<Textends Request, Qextends Response>{
protectedabstractclass AbstractTask<Rextends AbstractRequestExecutor>{
}
protectedabstractclass AbstractRequestExecutorimplements Runnable{
}
}
Is Eclipse or javac correct?
Thanks
Kaj
[2898 byte] By [
kajbja] at [2007-11-27 4:18:10]

# 1
This seems to be more of a problem with nested classes than with generics. Using 'static' in front of your class definitions will resolve the problem. (I'm using 1.5). Another option is to parameterize the class definition of AbstractRequestExecutor.
// in AbstractRequestExecutor
protected abstract class AbstractRequestExecutor<T, Q> implements Runnable {}
I think this is because you need both an AbstractRequestExecutor object (I know it's abstract) and a RequestHandler object to make a RequestExecutor object. It looks like the Sun javac doesn't automatically associate all of these objects, so a little extra information seems to clear things up if you don't use 'static' to do away with the object issue entirely.
# 2
> This seems to be more of a problem with nested
> classes than with generics.
I'm not sure that I agree with that. The example was simplified, but it showed the problem with the compiled.
Both AbstractTask and AbstractRequestExecutor needs to access method and attributes of the outer class, so I can't make them static.
Both classes do as well use R and Q, and AbstractTask calls methods in AbstractRequestExecutor. Making them static and add R and Q to them would read to other compiler errors since Q in one class isn't the same as Q in another class.
> Using 'static' in front
I can't do that see above.
> of your class definitions will resolve the problem.
> (I'm using 1.5). Another option is to parameterize
> the class definition of AbstractRequestExecutor.
>
> > // in AbstractRequestExecutor
> protected abstract class AbstractRequestExecutor<T,
> Q> implements Runnable {}
>
That creates compiler warnings, that T and Q are hiding T and Q in the outer class. And this will lead to the problem described above.
> I think this is because you need both an
> AbstractRequestExecutor object (I know it's abstract)
> and a RequestHandler object to make a RequestExecutor
> object. It looks like the Sun javac doesn't
> automatically associate all of these objects, so a
> little extra information seems to clear things up if
> you don't use 'static' to do away with the object
> issue entirely.
I can't.
Here's the same example but with some dummy code as well (the message classes are the same)
class XmlRequestHandler extends RequestHandler<XmlRequest, XmlResponse> {
//javac gives:
//type parameter XmlRequestHandler.RequestExecutor is not within its bound
class Task extends AbstractTask<RequestExecutor> {
@Override
protected List<RequestExecutor> getExecutors() {
return null;
}
}
class RequestExecutor extends AbstractRequestExecutor {
public void run() {
}
@Override
public XmlResponse getResponse() {
return null;
}
}
}
class RequestHandler<T extends Request, Q extends Response> {
protected abstract class AbstractTask<R extends AbstractRequestExecutor> {
public void execute() {
List<R> executors = getExecutors();
for (R executor : executors) {
//Use executor to send request
}
//Wait for all responses
for (R executor : executors) {
//check all respones
Q response = executor.getResponse();
//The line above should compile, and does if
//AbstractTask and AbstractRequestExecutor are
//inner classes, but it will fail if the classes are static
}
}
protected abstract List<R> getExecutors();
}
protected abstract class AbstractRequestExecutor implements Runnable {
public abstract Q getResponse();
}
}
Thanks for your time
Kaj
# 3
in my experience if it's a question of Eclipse being wrong or the JDK, my money is on Eclipse being wrong.
# 4
Strange, my experience when I was learning generics was just the opposite. I'm certainly having a hard time seeing why the original code shouldn't compile. Will be interesting to see the outcome.
# 5
I agree with the previous poster. As a second set of eyes, I can understand "Request", "Handler", and "Task", but when you throw in all the parameterized types, inheritance, and nesting, it's tough to decipher what's going on. Many smaller, unnested classes would acutally be clearer and there are dozens of design patterns for interrelating everything.
I think you should use the generics more sparingly, say limiting it to your getExecutors() method which returns a List of a parameterized type. There doesn't seem to be a reason to parameterize the request since getResponse() : Response seems to cover the bases.
Anyway, because of the nesting, the compiler doesn't see the AbstractRequestExecutor in the Task class definition as the same in the AbstractTask class definition. This is because the Task class is using "RequestHandler<XmlRequest,XmlResponse>.AbstractRequestExector" and the AbstractTask class definition is using "RequestHandler<T,Q>.AbstractRequestExecutor".
See the code below. I made sure that the two uses of AbstractRequestExecutor match. Of course this screws with the design, because your abstract class isn't general anymore, but you can't really go the other way.
import java.util.List;
interface Request {}
interface Response {}
class XmlRequest implements Request {}
class XmlResponse implements Response {}
class XmlRequestHandler extends RequestHandler<XmlRequest, XmlResponse> {
//javac gives:
//type parameter XmlRequestHandler.RequestExecutor is not within its bound
class Task extends AbstractTask<RequestExecutor> {
@Override
protected List<RequestExecutor> getExecutors() {
return null;
}
}
class RequestExecutor extends AbstractRequestExecutor {
public void run() {
}
@Override
public XmlResponse getResponse() {
return null;
}
}
}
class RequestHandler<T extends Request, Q extends Response> {
//protected abstract class AbstractTask<R extends RequestHandler><XmlRequest,XmlResponse>.AbstractRequestExecutor> {
protected abstract class AbstractTask<R extends RequestHandler><XmlRequest,XmlResponse>.AbstractRequestExecutor> {
public void execute() {
List<R> executors = getExecutors();
for (R executor : executors) {
//Use executor to send request
}
//Wait for all responses
for (R executor : executors) {
//check all respones
Response response = executor.getResponse();
//The line above should compile, and does if
//AbstractTask and AbstractRequestExecutor are
//inner classes, but it will fail if the classes are static
}
}
protected abstract List<R> getExecutors();
}
protected abstract class AbstractRequestExecutor implements Runnable {
public abstract Response getResponse();
}
}
# 6
> The code below> compiles fine in Eclipse, but javac barfs.At the risk of sounding naieve... doesn't Eclipse use javac?
# 7
> > The code below> > compiles fine in Eclipse, but javac barfs.> > At the risk of sounding naieve... doesn't Eclipse use> javac?No. It has it's own compiler. Kaj
# 8
> > The code below
> > compiles fine in Eclipse, but javac barfs.
>
> At the risk of sounding naieve... doesn't Eclipse use
> javac?
No. It uses ECJ - the Eclipse compiler for Java - by default. You can configure it to use, for example, javac, but it's not as simple as just changing a setting, and besides ECJ is a nice incremental compiler
# 9
> in my experience if it's a question of Eclipse being
> wrong or the JDK, my money is on Eclipse being wrong.
I recognize that from from my other thread where I had another compiler bug. (I'm still not sure if the bug was in Eclipse or javac, but it looks like that one was in javac)
http://forum.java.sun.com/thread.jspa?threadID=758690
# 10
> I think you should use the generics more sparingly,
> say limiting it to your getExecutors() method which
> returns a List of a parameterized type. There
> doesn't seem to be a reason to parameterize the
> request since getResponse() : Response seems to cover
> the bases.
As I said it's not the full code. Both classes are using both request and responses.
The purpose of the base class is to ease the development of different type of message handlers in a gateway that gets one request that should be sent to multiple servers at the same time. It then waits for all respones and merges the replies when they are received. The merged response is sent back to the caller.
I have removed some usage of generics but then I have to use casts, and I don't see that as a good option.
The beauty of the base class with generics is that new handlers that handles different types of messages just have to create subclasses of nested classes and use them in a few callback methods. The base class will handle all boiler plate code.
> Anyway, because of the nesting, the compiler doesn't
> see the AbstractRequestExecutor in the Task class
> definition as the same in the AbstractTask class
> definition. This is because the Task class is using
> "RequestHandler<XmlRequest,XmlResponse>.AbstractReques
> tExector" and the AbstractTask class definition is
> using "RequestHandler<T,Q>.AbstractRequestExecutor".
>
> See the code below. I made sure that the two uses of
> AbstractRequestExecutor match. Of course this screws
> with the design, because your abstract class isn't
> general anymore, but you can't really go the other
> way.
Yes I can. The base class does only use the methods in the interfaces. I had the generics there so that the methods in the subclasses wouldn't have to cast in their implementations. I removed some usage of generics and added casts to the subclasses (but I hate that when the code compiles fine in Eclipse, and also executes without errors)
Kaj
# 11
> in my experience if it's a question of Eclipse being
> wrong or the JDK, my money is on Eclipse being wrong.
I've always found that when it comes to generics, Eclipse is less fussy. Doesn't help much when your build process is javac-centric, mind you, it just seems there's always some unintuitive tweaking to be done to get javac to compile all your generics code
# 12
hi kajbj,i tried under ubuntu with netbeans and the problem occurs!i found this nice article http://www-03.ibm.com/developerworks/blogs/page/Wayner?entry=did_you_know_that_eclipseregards
# 13
> in my experience if it's a question of Eclipse being
> wrong or the JDK, my money is on Eclipse being wrong.
My impression has been that problems with generics compilation have been fairly evenly distributed between the two. This leads me to believe the problem is with Java generics.
# 14
> > in my experience if it's a question of Eclipse
> being
> > wrong or the JDK, my money is on Eclipse being
> wrong.
>
> My impression has been that problems with generics
> compilation have been fairly evenly distributed
> between the two. This leads me to believe the
> problem is with Java generics.
I'm waiting for Sun to check my bug report and see what they say. I will post the result in this thread.
Kaj