This discussion is archived
2 Replies Latest reply: Jul 22, 2010 4:23 AM by 843793 RSS

Odd behavior with generics

843793 Newbie
Currently Being Moderated
Hi everyone,

I'm attempting to write some classes making use of generics (in JDK 1.6), and I've stumbled upon an odd behavior. The classes are meant to represent a Finite State Machine, but I guess the problem would be the same if they were called Foo, Bar and such.
Here are my classes (stripped of all frills, one for each file of course).
public class Fsm
{
}

public class Session
{
}

public class SessionAwareFsm<SessionType extends Session> extends Fsm
{
    private SessionType session;

    public SessionType getSession()
    {
        return session;
    }
}

public class State<FsmType extends Fsm>
{
    private FsmType fsm;

    public FsmType getFsm()
    {
        return fsm;
    }
}

public class SessionAwareState<SessionType extends Session>
     extends State<SessionAwareFsm<SessionType>>
{
    public SessionType getSession()
    {
        return getFsm().getSession();
    }
}

public class MySession extends Session
{
    public void myMethod() {}
}

public class MyState extends SessionAwareState
{
    public void doSomething()
    {
        // The following instruction does not compile
        // Apparently, getFsm returns a Fsm, not a
        // SessionAwareFsm as expected
        Session s = getFsm().getSession();

        // This instruction works correctly
        Session t = getSession();
    }
}
The compilation problem described before baffles me. Since MyState is a SessionAwareState, and thus is based on a State<SessionAwareFsm>, I was expecting to be able to access an already cast SessionAwareFsm via the getFsm() method. This is not the case.
I've verified that this problem can be circumvented in two separate ways:

First Way

Removing the SessionType generic from the SessionAwareState class, as follows:
public class SessionAwareState extends State<SessionAwareFsm>
{
    public Session getSession()
    {
        return getFsm().getSession();
    }
}
This way, the MyState class compiles, but at expense of reduced functionality.

Second way

Restoring the original SessionAwareState class, and redefining the MySession class as follows:
public class MyState extends SessionAwareState<Session>
{
    public void doSomething()
    {
         // Now it works
        Session s = getFsm().getSession();
        Session t = getSession();
    }
}
This way the MyState class compiles. However, I find that the last definition of MyState is redundant, since the SessionAwareState already defines SessionType as extending a Session. Moreover, the getSession() methods in SessionAwareState does the same thing as the first instruction of doSomething(), and compiles correctly.

The second way could be an affordable tradeoff, but this behavior still bugs me.
All in all, could someone explain me why the compiling error with the original classes happens?

Thanks a lot for your time and patience. If you need any kind of additional information or explanation, do not hesitate to ask.
  • 1. Re: Odd behavior with generics
    843793 Newbie
    Currently Being Moderated
    Hello barabbas,

    SessionAwareState (sans type arguments), as the compiler should be telling you, is a raw type. For a definition of what that is, I refer you to the JLS, specifically:
    The superclasses (respectively, superinterfaces) of a raw type are the erasures of the superclasses (superinterfaces) of any of its parameterized invocations.
    It follows The Third Way
    public class SessionAwareState<SessionType extends Session> extends State<SessionAwareFsm<SessionType>> {
         public SessionType getSession() {
              return getFsm().getSession();
         }
         
         @Override
         public SessionAwareFsm<SessionType> getFsm() {
              return super.getFsm();
         }
    }
    With kind regards
    Ben
  • 2. Re: Odd behavior with generics
    843793 Newbie
    Currently Being Moderated
    Hello Ben,

    thanks a lot for your suggestion and explanations. I guess redefining the getFsm() method as you pointed out is a kind of "lesser evil", in this case.

    Regards,
    Barabbas