1

At the moment, I am preparing for an exam which I will write in two days. At the example-questions I found the following question:

Will this compile?

FavoritesList<Person> l = new FavoritesList<Contact>();

(Contact extends Person)

I would have answered yes, because if I have the following:

FavoritesList<Person> l = new FavoritesList<Person>();
l.add(new Contact());

that's fine. Why isn't it fine in the first example?

keep in mind: Contact extends Person!

Thanks in advance Best regards

Konstantin Yovkov
  • 62,134
  • 8
  • 100
  • 147
Christian
  • 609
  • 8
  • 22

7 Answers7

2

Well this is forbidden, but let me point why:

Let's consider:

FavoritesList<Person> l = new FavoritesList<Contact>();

There are operations that are allowed for FavoritesList<Person> but forbidden for FavoritesList<Contact> namely addition of any subclass of Person which breaks contract for FavoritesList<Contact>.

What you may be looking for is:

 FavoritesList<? extends Person> wildcardedList = new FavoritesList<Contact>();

which means: this is a list of some unspecified type ?, all elements in this list are of this type ?, and we know that this type ? is extending person. Beware that this type wildcards may be unintuitive at first. Basically what they give you is a read-only view of this list.

Lets assume:

 class FavoritesList<T>{

    void add(T t){...}

 }

Basically you can't call:

 wildcardedList.add(new Contact()); 

nor:

 wildcardedList.add(new Contact()); 

because we don't know whether Person or Contact is of unspecified type T.

To do that you'd have add wildcard to type of add parameter, and then it get's messy.

jb.
  • 23,300
  • 18
  • 98
  • 136
1

That's not fine because generics in Java don't support inheritance, meaning FavoritesList<Contact> is not a subtype of FavoritesList<Person>
See this: http://docs.oracle.com/javase/tutorial/java/generics/inheritance.html

TulaGingerbread
  • 435
  • 5
  • 15
0

No, it will not work. Java does not offer the property of covariance for generics, so if Contact extends Person this will not imply that FavoritesList<Contact> extends FavoritesList<Person>.

nanofarad
  • 40,330
  • 4
  • 86
  • 117
0

Imagine that you had another subclass of Person (e. g. Enemy, where Enemy extends Person).

Then if the first were allowed you could do:

FavoritesList<Contact> contacts = // a list of some Contacts
FavoritesList<Person> l = contacts;

// at this point, contacts and l both reference the same object!

l.add(new Enemy()); // allowed by the compiler since enemy is a person

// now contacts contains an enemy which is bad since Enemy is not a Contact!
Contact c = contacts.get(contacts.size() - 1); // would throw a class cast exception if the compiler allowed the above

Note that Java does provide a workaround for this with wildcards, so you can do:

FavoritesList<Contact> contacts = // a list of some Contacts
FavoritesList<? extends Person> l = contacts;

Person person = l.get(0); // ok since we know everything in l is some class that extends Person
l.add(new Enemy()); // won't compile, since we can't verify that Enemy is the ?
ChaseMedallion
  • 20,860
  • 17
  • 88
  • 152
0

It's not fine because allowing it would allow you to insert other types of Person into your list of Contacts. Whilst it's true that an immutable list of Contacts "is an" immutable list of Persons, it's not true that a mutable list of Contacts is a mutable list of Persons - you can insert non-Contacts into a mutable list of Persons, but not into a mutable list of Contacts.

As a result, Java forbids you from doing this sort of thing for safety reasons. What you could do is something like this:

FavoritesList<? extends Person> l = new FavoritesList<Contact>();

That would be valid, since you can't insert a non-Contact via a FavoritesList<? extends Person> and break your list.

Other languages (e.g. C#, Scala) allow you to provide covariance and contravariance annotations for your type parameters to gain more control over what conversions are allowed in this sort of situation (e.g. you can mark your type parameter as covariant if it is that, allowing Thing<Derived> to be converted to Thing<Base>), but Java doesn't. Here's a C# reference about the subject that might make things clearer:

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

Stuart Golodetz
  • 20,238
  • 4
  • 51
  • 80
0

In FavoritesList<Person> l = new FavoritesList<Contact>(); you created FavoritesList of Contacts.

But via reference FavoritesList<Person> l you will able to add instance of any kind of Person classes or classes that extends Person, like lets Employee to that list.

Do you think it would be safe?

Pshemo
  • 122,468
  • 25
  • 185
  • 269
0

There is a reason this does not compile and it can be observed with arrays.

  Object[] objects = new String[10];
  objects[0] = new Object();

Now each of these lines alone makes sense. However we end up inserting an Object into an array of String, which of course is wrong and throws an exception.

Generics where added later and the java developers decided to fix this, a List<String> is not compatible with List<Object> since both have different constraints on their contents, however they added an additional syntax so a List<String> is compatible with List<? extends Object> (read: a list that can contain an unknown type of Object).

    List<String> stringList = ...
    List<Object> objectList = stringList; //error
    List<? extends Object> someList = stringList;//string is a subtype of object
    stringList.add("Hello");//add string to stringlist, works
    someList.add("Hello");// error exact type of list unknown
    String h1 = stringList.get(0);//get string from StringList 
    String h2 = someList.get(0);//error, can be any subtype of object
    Object h3 = someList.get(0);//get object from someList
    String h4 = (String)someList.get(0);//we know that somelist has a string
josefx
  • 15,506
  • 6
  • 38
  • 63