user4780224
user4780224

Reputation:

Java Enum - Store Type For Later Cast

I have an enum representing all column values in a specific table. Now I want to store a type per enum element which is queried for later casting.

public enum Value {

    MEMBER1(?...?),
    MEMBER2(?...?);

    private final ?...? type;

    Value(?...? type) {
        this.type = type;
    }

    public ?...? getType() {
        return type;
    }

}

I don't now what I have to put instead of ?...?. Maybe its MyClassA.class and Class but how can I use this value for this operation:

MEMBER1.getType() instance = (MEMBER1.getType()) object;

This is really important for database communication with java, for me.

UPDATE: I mean this operation.

END UPDATE: There was is logic error. If I knew the different types I can simply create multiple methods because I have a api specification.

Upvotes: 4

Views: 9009

Answers (4)

scottb
scottb

Reputation: 10084

I have an enum representing all column values in a specific table. Now I want to store a type per enum element which is queried for later casting.

In general, this can't be done the way you want it to be done.

What you want is for the enum to hold the keys for a heterogenous type safe container (Essential Java, 2nd Ed.).

Unfortunately, Java Class objects are generified (eg. String.class is Class<String>) and Java enums and generics do not play well together.

While it is possible to store a Class object as a field within an enum, your Class object will lose its type information in the process. You will still need to manually reassert the type linkage when you access the field ... and this will require an unsafe cast. I don't know that there is any way around it.

For example, your enum may have a field named colClassToken in which you intend to store the type of the associated column in your table:

public enum TableColumn {
    MEMBER1(String.class),
    MEMBER2(Integer.class);

    private Class<?> e_colToken;

    TableColumn(Class<?> tok) { this.e_colToken = tok; }

    public Class<?> getColumnToken() { return this.e_colToken; }

    :
    :
}

What you would like to be able to do now is use the Class object's cast() method to reassert the type linkage when you access columns in your table (for example, using an idiom like):

String myColumn = MEMBER1.getColumnToken().cast(myTable.getCol(MEMBER1));

This won't compile, however. Because the type of the Class object has been lost in the enum field (and because you cannot generify an enum), the compiler is unable to guarantee the type safety of the cast() operation.

Upvotes: 0

czpona
czpona

Reputation: 320

You are right. The enum should be defined as follows

public enum Value {

   MEMBER1(String.class),
   MEMBER2(Integer.class);

   private final Class<?> type;
   private Value(Class<?> type) {
       this.type = type;
   }

   public Class<?> getType(){
       return this.type;
   }
}

The second part is actually something you don't want to do because you already know what is the type you want to cast your object to (In your case is it MyClassA) so you can cast directly like following

//I know what is the target type so I don't have to fetch the type from my enum
MyClassA instance = (MyClassA) object;

Once you will need to build your logic on unknown data type, than you can do something like

Object o = getTheObjectSomewhere();
if(o.getClass().isAssignableFrom(MEMBER1.getType()){
    String s = (String) o;
    System.out.println("Target type is string!");
    doSomething();
} else if (o.getClass().isAssignableFrom(MEMBER2.getType()){
    Integer i = (Integer) i;
    System.out.println("Target type is integer!");
    doSomethingElese();
}

Maybe you want to search the column later according to the type of your object. Then you can create a method to get the column

public Value getColumnByObjectType(Object o){
    for(Value v : Value.values()){
        if(v.getType().equals(o.getClass()){
            return v;
        }
    }
    return null;
}

And use it to search for the column

Object o = getObjectSomewhere();
Value column = getColumnByObjectType(o);

To get the user object according to the comment bellow you can do something like following using JPA

public Object getUserObject(int id, Value v){
    TypedQuery<?> q = DBUtils.getEntityManager().createQuery("select u.:column from User u where u.id = :id",v.getType());
    q.setParameter("column", v.toString());
    q.setParameter("id", id);
    q.setMaxResult(1);
    return q.getSingleResult();
}

Upvotes: 7

Tunaki
Tunaki

Reputation: 137084

You could do it by storing the Class of the object and use it to cast when necessary:

class Test {

    public static void main(String[] arguments) throws Exception {
        Object object = new MyClassA();
        Class<MyClassA> clazz = Value.MEMBER1.getType();
        MyClassA instance = clazz.cast(object);
        System.out.println(instance);
    }

}

enum Value {

    MEMBER1(MyClassA.class),
    MEMBER2(MyClassB.class);

    private final Class<?> clazz;

    Value(Class<?> clazz) {
        this.clazz = clazz;
    }

    @SuppressWarnings("unchecked")
    public <T> Class<T> getType() {
        return (Class<T>) clazz;
    }

}

class MyClassA { }
class MyClassB { }

Upvotes: 4

qwazer
qwazer

Reputation: 7521

how can I use this value for this operation:

MyClassA instance = (MEMBER1.getType()) object;

Use instanceof to check object type.

MyClassA instance;
if (object instanceof MyClassA && object instanceof MEMBER1.getType()){ 
 instance = (MyClassA) object;
}

Upvotes: 0

Related Questions