views:

87

answers:

3

I have some code that processes fixed length data records. I've defined the record structures using java enums. I've boiled it down the the simplest example possible to illustrate the hoops that I currently have to jump through to get access to a static variable inside the enum. Is there a better way to get at this variable that I'm overlooking? If you compile and run the code, it converts each string into an XML chunk. I've done the XML manually so that the example is simpler, but my real code uses Java DOM.

EDIT: I've updated the code to a more complete example that I think better shows what I'm trying to do.

class EnumTest
{
    public static void main(String args[])
    {
        System.out.println( parse("ABC", RecordType1.class) );
        System.out.println( parse("ABCDEF", RecordType2.class) );
    }

    private interface RecordLayout {
        public int length();
    }

    private enum RecordType1 implements RecordLayout {
        FIELD1      (2),
        FIELD2      (1),
        ;
        private int length;
        private RecordType1(int length) { this.length = length; }
        public int length() { return length; }

        public static int RECORD_LEN = 3;
    }

    private enum RecordType2 implements RecordLayout {
        FIELD1      (5),
        FIELD2      (1),
        ;
        private int length;
        private RecordType2(int length) { this.length = length; }
        public int length() { return length; }

        public static int RECORD_LEN = 6;
    }

    private static <E extends Enum<E> & RecordLayout> String parse(String data, Class<E> record) {

        // ugly hack to get at RECORD_LEN...
        int len = 0;
        try {
            len = record.getField("RECORD_LEN").getInt(record);
        }
        catch (Exception e) { System.out.println(e); }

        String results = "<RECORD length=\"" + len +  "\">\n";
        int curPos = 0;
        for (E field: record.getEnumConstants()) {
            int endPos = curPos + field.length();
            results += "  <" + field.toString() + ">"
                    + data.substring(curPos, endPos)
                    + "</" + field.toString() + ">\n";
            curPos = endPos;
        }
        results += "</RECORD>\n";
        return results;
    }
}
+1  A: 

Does RecordType1.LEN not work?

I don't have a Java environment at hand right now, so I'm preparing to wipe egg from my face, but since enum Foo is equivalent to class Foo extends Enum<Foo> I cannot see why you can't just access the static members in the standard way.

Edit: if you want to access static members dynamically, then this isn't going to work as you have to access static members based on the class rather than the instance, that's the point. You should probably do something like this instead, since if the behaviour changes depending on the subtype of the instance, that's exactly the kind of thing an interface is for:

class EnumTest
{  

    private interface RecordLayout {
        public int length();
        public int staticLength(); // give this a more meaningful name
    }

    private enum RecordType1 implements RecordLayout {
        ...

        public static int LEN = 3;
        public int staticLength() {
            return LEN;
        }
    }

    private enum RecordType2 implements RecordLayout {
        ...

        public static int LEN = 5;
        public int staticLength() {
            return LEN;
        }
    }

    ...

    private static String parse(String data, RecordLayout record) {
        int len = record.getStaticLength();
        System.out.println(len);

        ...
    }
}
Andrzej Doyle
The parse method is meant to be generic so that it can process any enum that implements the RecordLayout interface. In my actual code, there are many such enums. If I put RecordType1.LEN in my parse method, then it would be wrong if I called it with RecordType2.class as a parameter.
krick
In that case, you have to add another method to the `RecordLayout` interface, since the generic type parameters are only little more than syntactic sugar at compile time.At run time, they disappear in almost all cases.
Roland Illig
Then the problem is that you are passing in a class (record) rather than an instance of record. If you passed in an instance of record you could call: record.length().
staticman
If there is something you need to access from the RecordLayout interface, then you need to add it to the RecordLayout interface.
Ross
@RolandI've tried adding another method to the RecordLayout interface. The problem is that I can't call a static method because I don't have a class instance. So unless I'm missing something, that won't work either.
krick
@krick - then you're misunderstanding the point of static fields. If you want dynamic dispatch, then you'll need to make it an instance-level method (ideally on the RecordLayout interface) and have the different implementations of this method point to the relevant static fields. Static fields are when you want to refer to something... well, statically, as in "always the same".
Andrzej Doyle
@Andrzej - I fully understand the point of static fields. If you try it yourself, I think you'll find that it doesn't work the way you think in my code. I've tried adding a method to the interface and implementing it in each enum. I just can't access it from "record".
krick
At run time, your `parse` method doesn't know whether it was "generified" with `RecordLayout1` or `RecordLayout2`, because at that time, the generic types are all *erased*. It's like that in Java.
Roland Illig
See my edited answer for a proposal of how to do this.
Andrzej Doyle
Andrzej is right you are not using the static qualifier correctly, I don't see any reason to make that static. And I don't see any reason to pass in a Class instead of an instance of the Enum.
fuzzy lollipop
I just put a more complete example up there because I don't think I'm fully explaining what I'm trying to do. I want to be able to call a parse method and pass a string and a layout, and have the method carve up the string into the individual fields. It works great, except the part where I get the RECORD_LEN is kind of hacky. I just thought there was a better way that I was missing.
krick
@krick: Maybe each RecordType should do it's own parsing. I think you can find an easier, more OO way to do what you're trying if you look at the problem from a different perspective. If something is this hard to do, it's usually a sign that your approach is not correct.
Skip Head
I just figured out that I can pass the length to the parse method as a parameter using RecordLayout1.RECORD_LEN at the spot where I call parse(). I just can't get at it inside the method without a lot of work. I guess an extra parameter or two won't kill me.
krick
A: 

As long as you are inside the EnumTest class, you can access the LEN field by simply writing:

int len = RecordType1.LEN;

From the outside, it's still difficult.

Roland Illig
See my comment to Andrzej.
krick
A: 

I'll second Andrzej's answer. I have a Java environment and just tried it; RecordType1.LEN works fine for me.

Matt
See my comment to Andrzej.
krick