6

I got a small problem here regarding generics bounded type with lists. Please help out!

Model.java

public class Model {
}

ClassA.java

public class ClassA<T extends Model> {
    private List<T> models;

    public ClassA() {
        models.add((T) new Model());
    }
}

It gives me an unchecked cast from Model to T warning on this line:

models.add((T) new Model());

I understand I'm getting this warning because all I can safely cast from a sub class into a super class but not the other way round.

Is there any way to get over this problem or can I just safely supress the warning?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Terence Lam
  • 223
  • 4
  • 15
  • 3
    The T can be subclass of Model. So you cannot cast Model to its subclass. What do you want to do? – dev.brutus Sep 26 '13 at 15:23
  • why not `models.add(new T());`? – Cruncher Sep 26 '13 at 15:26
  • 1
    Note that even though you can cast and add it, you will get a `ClassCastException` when you try to use the elements in the list as `T` type objects where `T` is a subclass of `Model`. – Sotirios Delimanolis Sep 26 '13 at 15:26
  • 1
    @Cruncher `new T()` won't compile due to type erasure. – John Kugelman Sep 26 '13 at 15:27
  • You're trying to do it backwards. It's not safe to ignore that warning. Imagine class B that extends Model. Then declare ClassA (which is valid because B extends Model). Then try doing a case from your new Model to B. That will fail, because while B is a Model, a Model is not B. – user1676075 Sep 26 '13 at 15:29
  • If you want them to just always use Model, then you don't need Generics. – Cruncher Sep 26 '13 at 15:34

3 Answers3

12

You can't do what you're trying to do.

Since T is a subclass of Model:

  • every T is a Model
  • but not every Model is a T.

Specifically:

If you construct a new Model by calling new Model(), the instance is exactly a Model and not an instance of any subclass.

Where Subclass extends Superclass, you can never successfully do this:

(Subclass) new Superclass();

Because of this, you can not successfully cast a new Model to an instance of T.

The compiler will just give you a warning which you can either ignore or suppress, but you'll get a ClassCastException when you run your program and call the add() method.

James Dunn
  • 8,064
  • 13
  • 53
  • 87
2

Take this example

public static void main(String[] args) {
    ClassA<SubModel> models = new ClassA<>();
    for (SubModel model : models.getModels()){
        model.run(); // runtime ClassCastException
    }
}
public static class ClassA<T extends Model> {
    private List<T> models = new LinkedList<>();

    public ClassA() {
        models.add((T)new Model()); // compiles and warns
    }

    public List<T> getModels() {
        return models;
    }
}
public static class SubModel extends Model {
    public void run() {
        System.out.println(this);
    }
}
public static class Model {
}

The object you've added to models is of runtime type Model, which doesn't have a run() method.

When you eventually try to use the object as an object of type SubModel, you'll get a ClassCastException as I do in the main() method.

You really shouldn't do this, ie. add parent type objects to possible suptype type objects.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
-1

You need to initialize the list before adding elements in your constructor. I think you you might have copied only the relavant code snippet and thats why it was left out.

Also, I think the only way is to suppress warnings. More info only covariance, contravariance here : Covariance, Invariance and Contravariance explained in plain English?

Community
  • 1
  • 1
The Idler
  • 124
  • 1
  • 7
  • 1
    Suppressing the warning will only delay the inevitable `ClassCastException`. And it's true that the list needs to be `new`ed, but that's nothing to do with the `(T)` cast. – John Kugelman Sep 26 '13 at 15:29