dgorur
dgorur

Reputation: 1662

Dynamic casting in Java

Before I get chided for not doing my homework, I've been unable to find any clues on the multitude of questions on Java generics and dynamic casting.

The type Scalar is defined as follows:

public class Scalar <T extends Number> {

  public final String name;

  T value;

  ... 

  public T getValue() {
    return value;
  }

  public void setValue(T val) {
    this.value = val;      
  }

}

I would like to have a method that looks like this:

public void evilSetter(V val) {
  this.value = (T) val;
}

Sure, this is generally discouraged. The reason I want such a method is because I have a collection of Scalars whose values I'd like to change later. However, once they go in the collection, their generic type parameters are no longer accessible. So even if I want make an assignment that's perfectly valid at runtime, there's no way of knowing that it'll be valid at compile time, with or without generics.

Map<String, Scalar<? extends Number>> scalars = ...;

Scalar<? extends Number> scalar = scalars.get("someId");

// None of this can work
scalar.value = ...
scalar.setValue(...)

So how do I implement a checked cast and set method?

public <V extends Number> void castAndSet(V val) {
    // One possibility
    if (this.value.getClass().isAssignableFrom(val.getClass()) {
       // Some cast code here
    }
    // Another
    if (this.value.getClass().isInstanceOf(val) {
       // Some cast code here    
    }
    // What should the cast line be?
    // It can't be:
    this.value = this.value.getClass().cast(val);
    // Because this.value.getClass() is of type Class<?>, not Class<T>        

}

So I'm left with using

this.value = (T) val;

and catching a ClassCastException?

Upvotes: 3

Views: 13269

Answers (1)

Kirk Woll
Kirk Woll

Reputation: 77606

You have:

this.value.getClass().isAssignableFrom(val.getClass())

This is probably going to be a problem unless you can be certain value will never be null.

You also have:

this.value = (T) val;

This will only cast to Number and not to T because under the hood T is just a Number due to type-erasure. Therefore if value is a Double and val is an Integer, no exception will be thrown.

If you actually want to perform a checked cast, you must have the correct Class<T> object. This means you should be passing Class<T> in the constructor of your object. (Unless you can be sure value is never null, in which case you can go with your first idea.) Once you have that object (stored in a field), you can perform the checked cast:

T value = valueClass.cast(val);

Upvotes: 3

Related Questions