23

There are several other SO questions talking about generics compiling OK w/ Eclipse's compiler but not javac (i.e. Java: Generics handled differenlty in Eclipse and javac and Generics compiles and runs in Eclipse, but doesn't compile in javac) -- however this looks like a slightly different one.

I have an enum class:

public class LogEvent {
   public enum Type {
       // ... values here ...
   }
   ...
}

and I have another class with a method that takes in arbitrary objects of types descended from Enum:

@Override public <E extends Enum<E>> void postEvent(
    Context context, E code, Object additionalData) 
{
    if (code instanceof LogEvent.Type)
    {
        LogEvent.Type scode = (LogEvent.Type)code;
    ...

This works fine in Eclipse, but when I do a clean built with ant, I am getting a pair of errors, one on the instanceof line, the other on the casting line:

443: inconvertible types
    [javac] found   : E
    [javac] required: mypackage.LogEvent.Type
    [javac]         if (code instanceof LogEvent.Type)
    [javac]             ^

445: inconvertible types
    [javac] found   : E
    [javac] required: com.dekaresearch.tools.espdf.LogEvent.Type
    [javac]             LogEvent.Type scode = (LogEvent.Type)code;
    [javac]                                                  ^

Why does this happen, and how can I get around this problem so it will compile properly?

Community
  • 1
  • 1
Jason S
  • 184,598
  • 164
  • 608
  • 970

4 Answers4

32

I don't know why it's happening, but a workaround is easy:

@Override public <E extends Enum<E>> void postEvent(
    Context context, E code, Object additionalData) 
{
    Object tmp = code;
    if (tmp instanceof LogEvent.Type)
    {
        LogEvent.Type scode = (LogEvent.Type)tmp;
    ...

It's ugly, but it works...

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
8

Perhaps it is because you've declared E as something that extends Enum<E>. I can't say I understand it completely, but it looks like it limits the set of types to some subset that can't include LogEvent.Type for some reason. Or maybe it's just a bug in the compiler. I'd be happy if someone could explain it more clearly, but here is what you can do:

public <E extends Enum<?>> void postEvent(E code) 
{
    if (code instanceof LogEvent.Type)
    {
        LogEvent.Type scode = (LogEvent.Type)code;
        ...
    }
    ...

This works and it is more elegant than just casting to an Object.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
  • 1
    it works because "E extends Enum" refers to some specific subclass of Enum (which may not be compatible with LogEvent.Type). whereas "E extends Enum>" refers to any class which extends Enum, of which LogEvent.Type is a valid possibility. – jtahlborn Jan 28 '11 at 16:35
  • @jtahlborn: The `E extends Enum` is standard boilerplate for any class that properly extends Enum, which all `enum` classes do, and `LogEvent.Type` is an `enum` class, so it should work as well, but there seems to be a bug in `javac`. – Jason S Jan 28 '11 at 16:40
  • @jtahlborn, yes, think of it - does LogEvent.Type extend Enum? Surely it does. But it's exactly what we mean when we write "E extends Enum", with E = LogEvent.Type. So I agree that it looks like a bug in javac. In fact, "E extends Enum>" and "E extends Enum" should mean exactly the same thing because any enum type E can only extend Enum and nothing else. – Sergei Tachenov Jan 28 '11 at 16:53
4

I had a similar problem and upgraded from jdk1.6.0_16 to jdk1.6.0_23 and it went away without any code changes.

WW.
  • 23,793
  • 13
  • 94
  • 121
0

In order to use instanceof both operands have to inherit/implement the same class/interface.
E just can't be cast to LogEvent.Type

I don't know what your full method looks like, but this should solve your issue by using interfaces and not Generics.

public interface EventType { }
public class LogEvent  {
    public enum Type implements EventType {}
}

public void postEvent(Context context, EventType code, Object additionalData) {
    if(code instanceof LogEvent.Type) {
    }
}
jonescb
  • 22,013
  • 7
  • 46
  • 42
  • But I want Enum to be the base type, not EventType. I need my method to accept arbitrary Enum values. – Jason S Jan 28 '11 at 15:24
  • But why E can't be cast to LogEvent.Type? E is a subclass of Enum, and LogEvent.Type is a subclass of Enum as well. I understand that the problem part is that it's not just Enum, but Enum, I just can't understand what that means exactly. – Sergei Tachenov Jan 28 '11 at 15:24
  • The other SO questions on this topic seem to indicate that `javac` has a known bug with recursive generic bounds, so maybe it gets confused. With respect to the `>`, if you haven't seen it before: http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#FAQ106 – Jason S Jan 28 '11 at 16:01