Paul Jackson
Paul Jackson

Reputation: 2147

Why does Java force generic types to be cast?

I do not understand why the compiler can not see that a cast is safe when the parameterized type is defined as extending a base class. Here are examples of casts that seem to me like they should be unnecessary. Further, when I do include the cast, my IDE (IntelliJ IDEA) warns that the cast is unchecked, as if to suggest that I am doing something wrong. Is there an idiom that avoids these casts and warnings? Why are the casts needed at all, given that the declaration states that the type extends the base class?

class Shape {}

class Polygon extends Shape {}

public class Foo<T extends Shape>
{
  Set<Polygon> polygons;
  // Why must this be cast?
  Set<T> shapes = (Set<T>) new HashSet<Polygon>(); 

  T getFirst()
  {
    // Why must this be cast?
    return (T) polygons.iterator().next();
  }

  Iterable<T> getShapes()
  {
    // Why must this be cast?
    return (Iterable<T>) polygons;
  }
}

Upvotes: 2

Views: 1216

Answers (6)

newacct
newacct

Reputation: 122439

// Why must this be cast?
Set<T> shapes = (Set<T>) new HashSet<Polygon>(); 

This is the least of your problems. The conversion is actually logically incorrect. Set<A> is not a subtype of Set<B> if A and B are different, even if A is a subtype of B. If we had reifiable generics this cast would fail.

Upvotes: 1

Ravi Bhatt
Ravi Bhatt

Reputation: 3163

Set<T> shapes = (Set<T>) new HashSet<Polygon>();

A cast is needed as T here can be anything that extends Shape and you are trying to fit only Polygons. Circle is a shape but its not Polygon. The best practice is to treat parameterized generics as a unique class.

If java allowed the above without the cast, it would then be opening a door for adding any T to the set. Imagine you assigned your Polygon set to a Set<T> and then added Circle objects to it. That's inviting lots of runtime problems.

Upvotes: 1

Matt Fenwick
Matt Fenwick

Reputation: 49085

You may be interested in reading this about Java generics.

Basically,

  Box<Integer> and Box<Double> are not subtypes of Box<Number>

Upvotes: 1

Alexander Pogrebnyak
Alexander Pogrebnyak

Reputation: 45576

Let's assume you've instantiated your class like this:

Foo<Circle> circleFoo = new Foo<Circle>( );

Then, Set<Circle> cannot be safely assigned HashSet<Polygon>

In getFirst: You cannot safely cast Polygon to Circle

And in getShapes: you cannot safely cast Iterable<Polygon> to Iterable<Circle>.

Upvotes: 5

Molochdaa
Molochdaa

Reputation: 2218

T extends Shape, Polygon extends Shape. So there is no reason that T extends Polygon

Upvotes: 5

Oliver Charlesworth
Oliver Charlesworth

Reputation: 272487

In the first example, Set<T> is not a base class of HashSet<Polygon>.

In the second example, the type of polygons.iterator().next() is Polygon, which is not the same as T.

Upvotes: 0

Related Questions