java: How can I do dynamic casting of a variable from one type to another?

224,061

Solution 1

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.

Solution 2

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 sense since 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 to do 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 a 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)

Solution 3

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.

Solution 4

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);
    }
}

Solution 5

You can write a simple castMethod like the one below.

private <T> T castObject(Class<T> clazz, Object object) {
  return (T) object;
}

In your method you should be using it like

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

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

}
Share:
224,061
ufk
Author by

ufk

Programmer, Administrator, Gamer and Musician

Updated on March 16, 2020

Comments

  • ufk
    ufk about 4 years

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

    This is the regular casting:

     String a = (String) 5;
    

    This is what I want:

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

    Is this 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 class' variables are of type Double, and if the number 3 is received it sees it as Integer and I have type problem.

  • ufk
    ufk over 14 years
    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.
  • Sandeep Chauhan
    Sandeep Chauhan over 14 years
    Exactly, in Java you cannot be that dynamic, see my update as well.
  • Sandeep Chauhan
    Sandeep Chauhan over 14 years
    See BalusC's answer below, this is the length (and pain) to which you have to go...
  • srini.venigalla
    srini.venigalla over 14 years
    @BalusC - I find the ObjectConverter code interesting, could you please describe the use cases for it?
  • BalusC
    BalusC over 14 years
    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
    BalusC over 14 years
    What if the entry type isn't a supertype of the field type at all? You'll then really need to convert programmatically.
  • Michael Borgwardt
    Michael Borgwardt about 11 years
    @name: concerning the edit you keep suggesting: note that I am talking about not about primitive values but about the wrapper classes (signified by the capitalization and the styling as code), and casting between those is definitely not possible.
  • ruiruige1991
    ruiruige1991 over 5 years
    Yes, I think this answer is what the asker want. He/She just get a Class<?>, and wants to convert an instance to the class "?". By default java do not support this. But using <T> we can do this.
  • George Xavier
    George Xavier over 4 years
    I know this is late but I think he meant [thetype] a = (thetype)some_object; which is pointless because you can just do Object a = some_object fine
  • George Xavier
    George Xavier over 4 years
    @ruiruige1991 This is wrong. T in which case is a generic. Generics do nothing during runtime. (T)blah would just be (Object)blah during runtime because of type erasure. In short, generics -> compile-time, and has no effect during runtime. Since dynamic -> runtime and generics -> compile time, generics are useless.
  • George Xavier
    George Xavier over 4 years
    You shouldn't need to dynamically cast ever. Your formula of having a superclass with the method doSomething() and subclasses that implement the method doSomething() is one of the main purposes of OOP, polymorphism. Object foo = new Integer("1"); foo.toString() returns 1. Even though it is assigned to Object, it is an Integer, and therefore behaves as such. Running the method toString will run the Integer implementation. Polymorphism.