Reputation: 1662
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
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