Reputation: 1193
I have a list of some object
List<MyObject>
But I know it to be a list of some object that inherits from MyObject
List<ChildObject>
Trying to cast from one to the other gives me an "Unconvertible Types" warning. However, if I first cast to object and then cast to a list cast to the second type then the warning goes away
List<ChildObject> myChildList = (List<ChildObject>) (Object) myObjectList
This seems to work fine. Is there any reason I shouldn't be doing this? Could it throw an error, is there a better way or is it considered bad practice?
Upvotes: 3
Views: 141
Reputation: 24157
Generics were primarily designed for safe type checking at compile time. And they should not really be mixed with casting as casting pospones this type-safety check to runtime. Consider the following example where Vehicle
is a base class extended by various classes: Bike
, Car
etc.
List<Vehicle> vehicles = new ArrayList<>();
vehicles.add(new Bike("Yamaha"));
vehicles.add(new Car("VW"));
vehicles.add(new Car("BMW"));
List<Car> cars = (List<Car>)(Object)vehicles;
for (Car vehicle : cars) {
System.out.println(vehicle.getType());
}
Here I am only iterating over List<Car>
but you can see that this program can lead to fatal errors as the first item is not a Car
but a Bike
.
Upvotes: 0
Reputation: 30285
The problem is that while MyObject
and ChildObject
are related, List<MyObject>
and List<ChildObject>
are not, that's why you get the error.
Your casting solution works, but it throws type safety out the window, which can lead to bugs. The correct way of doing the transition from List<MyObject>
to List<ChildObject>
is to define the list with a wildcard:
//From Child to parent
List<? extends ChildObject> child1 = new ArrayList<>();
List<? extends MyObject> parent1 = child1;
//From parent to child
List<? extends MyObject> parent2 = new ArrayList<>();
List<? extends ChildObject> child2 = (List<? extends ChildObject>) parent2;
You can read more about it in this Java tutorial.
Upvotes: 1
Reputation: 726499
Is there any reason I shouldn't be doing this?
Generic type parameter on the list is there to help compiler do type checking for you at compile time. Casting throws away this check, moving the check to run time.
Could it throw an error?
Yes, it could, if the list contains objects other than ChildObject
. The worst part is that the cast could fail in some entirely unrelated place, where your code has no cast at all.
For example, if you do this
// Do the hack;
List<ChildObject> myChildList = (List<ChildObject>) (Object) myObjectList;
...
// Iterate over your list
for (ChildObject c : myChildList) {
...
}
In the example above, if myObjectList
has some classes other than ChildObject
, there would be a class cast exception at runtime. However, it would happen in the header of the for
loop, not at the point where you have your cast.
Is there a better way, or is it considered bad practice?
A better way would be creating a collection of ChildObject
s, and populating it with the content of the original array.
Upvotes: 3
Reputation: 48252
Return List<? extends MyObject>
where you are returning List<MyObject>
now. This would provide for avoiding typecasts
Upvotes: 3
Reputation: 65813
If you could do what you want to do without casting then this is an abuse of casting. If the result of your casting causes a run-time error then this is an abuse of casting.
Since generics was added to Java, casting is mostly unnecessary - casting should be kept to a minimum because it hides errors that could be found at compile time until run time.
Upvotes: 0