views:

1149

answers:

7

Hiya.

i would like to do dynamic casting for a java variable, the casting type is stored in a different variable.

this is regular casting:

 String a = (String) 5;

this is what i want:

 String theType = 'String';
 String a = (theType) 5;

is it possible? and if so how? thanks!

update

I'm trying to populate a class with a hashMap that i received.

this is the constructor:

public ConnectParams(HashMap<String,Object> obj) {

    for (Map.Entry<String, Object> entry : obj.entrySet()) {
        try {
            Field f =  this.getClass().getField(entry.getKey());                
            f.set(this, entry.getValue()); /* <= CASTING PROBLEM */
        } catch (NoSuchFieldException ex) {
            log.error("did not find field '" + entry.getKey() + '"');
        } catch (IllegalAccessException ex) {
            log.error(ex.getMessage());         
        }
    }

}

the problem here is that some of the classes variables are Double type, and if the number 3 is received it sees it as Integer and i have type problem.

+5  A: 

Regarding your update, the only way to solve this in Java is to write code that covers all cases with lots of if and else and instanceof expressions. What you attempt to do looks as if are used to program with dynamic languages. In static languages, what you attempt to do is almost impossible and one would probably choose a totally different approach for what you attempt to do. Static languages are just not as flexible as dynamic ones :)

Good examples of Java best practice are the answer by BalusC (ie ObjectConverter) and the answer by Andreas_D (ie Adapter) below.


That does not make sense, in

String a = (theType) 5;

the type of a is statically bound to be String so it does not make any sense to have a dynamic cast to this static type.

PS: The first line of your example could be written as Class<String> stringClass = String.class; but still, you cannot use stringClass to cast variables.

Adrian
I hope that the updated that i posted will explain what i'm trying to do. i come from a php background so maybe this thing is not possible to achieve in java.
ufk
Exactly, in Java you cannot be that dynamic, see my update as well.
Adrian
Thank you for clarifying this issue! :)
ufk
See BalusC's answer below, this is the length (and pain) to which you have to go...
Adrian
+7  A: 

You'll need to write sort of ObjectConverter for this. This is doable if you have both the object which you want to convert and you know the target class to which you'd like to convert to. In this particular case you can get the target class by Field#getDeclaringClass().

You can find here an example of such an ObjectConverter. It should give you the base idea. If you want more conversion possibilities, just add more methods to it with the desired argument and return type.

BalusC
@BalusC - I find the ObjectConverter code interesting, could you please describe the use cases for it?
srini.venigalla
It's useful in cases when convention over configuration is preferred and the source type doesn't match the target type. I have used it 2-3 years ago in my (pure for hobby purposes) ORM and MVC frameworks. Also see the leading text of the blog article.
BalusC
+1  A: 

It works and there's even a common pattern for your approach: the Adapter pattern. But of course, (1) it does not work for casting java primitives to objects and (2) the class has to be adaptable (usually by implementing a custom interface).

With this pattern you could do something like:

Wolf bigBadWolf = new Wolf();
Sheep sheep = (Sheep) bigBadWolf.getAdapter(Sheep.class);

and the getAdapter method in Wolf class:

public Object getAdapter(Class clazz) {
  if (clazz.equals(Sheep.class)) {
    // return a Sheep implementation
    return getWolfDressedAsSheep(this);
  }

  if (clazz.equals(String.class)) {
    // return a String
    return this.getName();
  }

  return null; // not adaptable
}

For you special idea - that is impossible. You can't use a String value for casting.

Andreas_D
+2  A: 

Your problem is not the lack of "dynamic casting". Casting Integer to Double isn't possible at all. You seem to want to give Java an object of one type, a field of a possibly incompatible type, and have it somehow automatically figure out how to convert between the types.

This kind of thing is anathema to a strongly typed language like Java, and IMO for very good reasons.

What are you actually trying to do? All that use of reflection looks pretty fishy.

Michael Borgwardt
+3  A: 

Yes it is possible using Reflection

    Object something = "something";
    String theType = "java.lang.String";
    Class<?> theClass = Class.forName(theType);
    Object obj = theClass.cast(something);

but that doesn't make much sensesince the resulting object must be saved in a variable of Object type. If you need the variable be of a given class, you can just cast to that class.

If you want to obtain a given class, Number for example:

    Object something = new Integer(123);
    String theType = "java.lang.Number";
    Class<? extends Number> theClass = Class.forName(theType).asSubclass(Number.class);
    Number obj = theClass.cast(something);

but there is still no point doing it so, you could just cast to Number.

Casting of an object does NOT change anything; it is just the way the compiler treats it.
The only reason doing something like that, is to check if the object is an instance of the given class or of any subclass of it, but that would be better done using instanceof or Class.isInstance().

Update

according your last update the real problem is that you have an Integer in your HashMap that should be assigned to an Double. What you can do in this case, is check the type of the field and use the xxxValue() methods of Number

        ...
        Field f =  this.getClass().getField(entry.getKey());
        Object value = entry.getValue();
        if (Integer.class.isAssignableFrom(f.getType())) {
            value = Integer.valueOf(((Number) entry.getValue()).intValue());
        } else if (Double.class.isAssignableFrom(f.getType())) {
            value = Double.valueOf(((Number) entry.getValue()).doubleValue());
        } // other cases as needed (Long, Float, ...)
        f.set(this, value);
        ...

(not sure if I like the idea of having the wrong type in the Map)

Carlos Heuberger
A: 

Don't do this. Just have a properly parameterized constructor instead. The set and types of the connection parameters are fixed anyway, so there is no point in doing this all dynamically.

Bandi-T
yeah that's a good point
ufk
+1  A: 

You can do this using the Class.cast() method, which dynamically casts the supplied parameter to the type of the class instance you have. To get the class instance of a particular field, you use the getType() method on the field in question. I've given an example below, but note that it omits all error handling and shouldn't be used unmodified.

public class Test {

    public String var1;
    public Integer var2;
}

public class Main {

    public static void main(String[] args) throws Exception {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("var1", "test");
        map.put("var2", 1);

        Test t = new Test();

        for (Map.Entry<String, Object> entry : map.entrySet()) {
            Field f = Test.class.getField(entry.getKey());

            f.set(t, f.getType().cast(entry.getValue()));
        }

        System.out.println(t.var1);
        System.out.println(t.var2);
    }
}
Jared Russell
What if the entry type isn't a supertype of the field type at all? You'll then really need to convert programmatically.
BalusC