views:

401

answers:

3

Hi,

I am trying to do something along the lines of:

public void setContents(Object[] values)
{
    ...

        //A. this works
        mRank =
            ((String)(values[Columns.RANK.index])); 

        //B. doesn't work (entire line underlined by netbeans)
        mRank =
            (Columns.RANK.type.cast(values[Columns.RANK.index]));
        //incompatible types: required java,lang.String found: java.lang.Object

        //C. doesn't work (first RANK is underlined by netbeans)
        mRank =
            ((Columns.RANK.type)(values[Columns.RANK.index]));
        //cannot find symbol symbol: class RANK location: blah.blah.Columns

    ...
}

Where columns is an inner enum, like so:

public static enum Columns
{

    RANK(0, "Rank", String.class),
    NUMBER(1, "Number", Integer.class);

    public String text;
    public Class type;
    public int index;

    private Columns(int idx, String text, Class clasz)
    {
        this.type = clasz;
        this.text = text;
        this.index = idx;
    }
}

I understand why line B doesn't work, but what I don't get is why C doesn't work. If I use Columns.RANK.type anywhere else other than in a type cast, it works fine, but one I attempt to do a typecast with the class, it compiles saying it cannot find RANK in the enum, which shouldn't be the case.

How to work around?

Thanks!

+1  A: 

C doesn't work, because Columns.RANK.type is not accessible at compile time.

However, B can be implemented using a custom generic-based class instead of enum:

class Columns<T>
{
    public static final Columns<String> RANK = new Columns<String>(0, "Rank", String.class);
    public static final Columns<Integer> NUMBER = new Columns<Integer>(1, "Number", Integer.class);

    public final Class<T> type;
    public final String text; 
    public final int index; 

    private Columns(int idx, String text, Class<T> clasz) 
    { 
        this.type = clasz; 
        this.text = text; 
        this.index = idx; 
    } 
}
axtavt
@axtavt, excellent workaround! I'm still waiting on another another to see if it is indeed possible to accomplish this using an `enum`, else the tick goes to you.
bguiz
@axtavt, just FYI, the class `Columns<T>` needed to be declared as `public static class Column<T>`, in order for it to work as suggested - inner classes cannot have static members unless they themselves are static.
bguiz
A: 

As it references a class literal, the expression Columns.RANK.type is of type Class<String>. It is not a ReferenceType, required by a cast expression.

Addendum: The compiler think's you meant to reference a non-existent class named RANK that is nested in Columns.

trashgod
+1  A: 

The short answer is that there's no good way to do this with an enum. axtavt's answer is probably your best bet. trashgod is basically right about why C doesn't work, but perhaps it could use a bit more explanation.

You need to think about how the compiler is interpreting C. What's important here is the distinction between String and String.class. You've got a casting expression like (String)foo. In such an expression, the type that you're casting to (in that example, String) has to be the name of a type. You wouldn't write (String.class)foo, because there is no class called String.class. (Instead, String.class is just an object, an instance of java.lang.Class that reflects the type String.)

So, when the compiler sees (Columns.RANK.type)(values[Columns.RANK.index]), it says "ah, this is a casting expression. So, the bit in parens at the beginning must be the name of the type that bguiz wants to cast to." Then it dutifully goes off and looks for a type named Columns.RANK.type. Since it's the name of a type, it's expecting it to be of the form my.package.containing.a.Type.AndMaybe.SomeInnerTypes. Thus, it splits it up around the .s, finds the type Columns, and then goes off and looks for an inner type called RANK in Columns. There is no such inner type (the constant RANK doesn't count), so it fails with the error you quoted.

(If it found that, it would continue looking for another inner type called type, and again, the field in the enum wouldn't count.)

Remember that the compiler is just following a bunch of dumb rules. It doesn't care that you also have a constant RANK in your Columns enum. It also doesn't know that type names are usually upper-case. As a result its error messages are sometimes hard to interpret for a human who is carrying around all that context in his head. :)

Matt McHenry
+1 : Very nice explanation of why compiler is spitting out the error that it is.
bguiz