datafiddler
datafiddler

Reputation: 1835

Extending enum attributes

I have a simple enum like this

public enum TypeId {
    Unknown(0),
    I1(1),
    I2(2),
    I4(3),
    I8(4),
    Ui1(5),
    Ui2(6),
    Ui4(7) 
    // ...
    ;
    private final int ID;
    private TypeID (int id) { ID = id; }
    public int getID () { return ID;  }
    public static TypeID getTypeID(int id) { ... }
};

And a more complicated one, containing more data, but covering the same elements related via the same id number.

public enum DataType {
    T_Null(0, "NULL", Types.NULL, 0, 0),
    T_Int8(1, "TINYINT", Types.TINYINT, 1, 4), 
    T_Int16(2, "SMALLINT", Types.SMALLINT, 2, 6), 
    T_Int32(3, "INTEGER", Types.INTEGER, 4, 11),
    T_Int64(4, "BIGINT", Types.BIGINT, 8, 20), 
    T_UInt8(5, "BYTE", Types.TINYINT, 1, 3), // 0 -> 255
    T_UInt16(6, "USHORT", Types.SMALLINT, 2, 5), // 0 -> 65535
    T_UInt32(7, "UINT", Types.INTEGER, 4, 10) // 0 -> 4294967295  
    // ...
    ; 
    private final int typeCode, sqlTypeCode, binSize, dispSize;
    private String typeName;

    private DataType(int typeCode, String typeName, int sqlTypeCode, int binSize, int dispSize) {
      this.typeCode = TypeCode;
      this.typeName = TypeName;
      this.sqlTypeCode = sqlTypeCode;
      this.binSize = binSize;
      this.dispSize = dispSize;
    }
    public String getName() { return typeName; }
    // other getters 
};

Now the first enum is given, and the second one is a kind of extension. Can this be implemented using extends or similar?

I want to achieve that the link via the same (ID == typeCode) is maintained / checked automatically.

One idea is to replace the int typeCode by a TypeId contained Element.

Questions:

Upvotes: 1

Views: 90

Answers (3)

Ian Mc
Ian Mc

Reputation: 5829

You are not able to extend an enum, however composition achieves a similar result.

public enum DataType {
    T_Null(TypeId.Unknown, "NULL", Types.NULL, 0, 0),
    T_Int8(TypeId.I1, "TINYINT", Types.TINYINT, 1, 4), 
    T_Int16(TypeId.I2, "SMALLINT", Types.SMALLINT, 2, 6), 
    T_Int32(TypeId.I4, "INTEGER", Types.INTEGER, 4, 11),
    T_Int64(TypeId.I8, "BIGINT", Types.BIGINT, 8, 20), 
    T_UInt8(TypeId.Ui1, "BYTE", Types.TINYINT, 1, 3), // 0 -> 255
    T_UInt16(TypeId.Ui2, "USHORT", Types.SMALLINT, 2, 5), // 0 -> 65535
    T_UInt32(TypeId.Ui4, "UINT", Types.INTEGER, 4, 10) // 0 -> 4294967295  
    ; 
    private final TypeId typeId;
    private final int sqlTypeCode, binSize, dispSize;
    private final String typeName;

    private DataType(TypeId typeId, String typeName, int sqlTypeCode, int binSize, int dispSize) {
      this.typeId = typeId;
      this.typeName = typeName;
      this.sqlTypeCode = sqlTypeCode;
      this.binSize = binSize;
      this.dispSize = dispSize;
    }
    public String getName() { return typeName; }
    // other getters 

    public static DataType ofType(TypeId typeId) {
        for (DataType dt : values())
            if (dt.typeId == typeId)
                return dt;
        return T_Null;
    }

};

Presumably you are motivated to use an existing API more naturally. If the existing apis are:

public TypeId someApi(int i) {  ....  }

public void myApi(DataType dt) { ... you prefer to use DataType ... }

Then you now have a natural way to invoke myApi as follows:

myApi(DataType.ofType(someApi(5)));

Upvotes: 1

Serge Ballesta
Serge Ballesta

Reputation: 148940

In fact the ID (resp. typeCode) is nothing more than an explicit equivalent of the implicit ordinal() of the enum.

That means that you can easily ensure that you have the same number of values in both enums. If they are different that means that the enums are not coherent. You could simply add a static bloc in another class that you are sure to load (for example the one containing main if it is relevant):

static {
    if (DataType.values().length != TypeID.values().length) {
        throw new Error("Different lengths");
    }
}

You could additionally if you want to keep the explicit ID ensure that it is equal to the ordinal:

enum TypeID {
    ...
    private TypeID (int id) {
        ID = id;
        if (id != ordinal()) throw new Error("ID out of sequence");
    }
}

That way you can make sure that both enums have coherent ids.

Upvotes: 1

OldCurmudgeon
OldCurmudgeon

Reputation: 65811

The simplest approach would be to make both enums implement an interface. Obviously you still don't have the full safety checks and uniqueness becomes your problem but it is one solution.

public interface HasId {
    int getID ();
}

public enum TypeID implements HasId{
    Unknown(0),
    I1(1),
    I2(2),
    I4(3),
    I8(4),
    Ui1(5),
    Ui2(6),
    Ui4(7)
    ;
    private final int ID;
    private TypeID (int id) { ID = id; }
    @Override
    public int getID () { return ID;  }
};

public enum DataType implements HasId{
    T_Null(0, "NULL", Types.NULL, 0, 0),
    T_Int8(1, "TINYINT", Types.TINYINT, 1, 4),
    T_Int16(2, "SMALLINT", Types.SMALLINT, 2, 6),
    T_Int32(3, "INTEGER", Types.INTEGER, 4, 11),
    T_Int64(4, "BIGINT", Types.BIGINT, 8, 20),
    T_UInt8(5, "BYTE", Types.TINYINT, 1, 3), // 0 -> 255
    T_UInt16(6, "USHORT", Types.SMALLINT, 2, 5), // 0 -> 65535
    T_UInt32(7, "UINT", Types.INTEGER, 4, 10) // 0 -> 4294967295  
    // ...
    ;
    private final int typeCode, sqlTypeCode, binSize, dispSize;
    private String typeName;

    private DataType(int typeCode, String typeName, int sqlTypeCode, int binSize, int dispSize) {
        this.typeCode = typeCode;
        this.typeName = typeName;
        this.sqlTypeCode = sqlTypeCode;
        this.binSize = binSize;
        this.dispSize = dispSize;
    }
    public String getName() { return typeName; }
    // other getters
    @Override
    public int getID () { return typeCode;  }
};

I would probably use a TypeId instead of int for the typeCode of your DataType enum.

Upvotes: 1

Related Questions