6

I have a method that takes a list of SResource objects

public static List<STriple> listTriples(List<SResource> subjects){
//... do stuff
}

Why can't I do this

List<IndexResource> resultsAsList = new ArrayList<IndexResource>();
    resultsAsList.addAll(allResults.keySet()); // I could possible not use lists and just use sets and therefore get rid of this line, but that is a different issue
List<STriple> triples = new ArrayList<STriple>();
    triples = TriplesDao.listTriples(resultsAsList);

(The compiler tells me I have to make triples use SResource objects.)

When IndexResource is a subclass of SResource

public class IndexResource extends SResource{ 
// .... class code here
}

I would have thought this has to be possible, so maybe I am doing something else wrong. I can post more code if you suggest it.

Ankur
  • 50,282
  • 110
  • 242
  • 312

3 Answers3

23

You can do it, using wildcards:

public static List<STriple> listTriples(List<? extends SResource> subjects){
    //... do stuff
}

The new declaration uses a bounded wildcard, which says that the generic parameter will be either an SResource, or a type that extends it.

In exchange for accepting the List<> this way, "do stuff" can't include inserting into subjects. If you're just reading from the subjects in the method, then this change should get you the results you want.

EDIT: To see why wildcards are needed, consider this (illegal in Java) code:

List<String> strings = new ArrayList<String>();
List<Object> objList = string; // Not actually legal, even though string "is an" object
objList.add(new Integer(3)); // Oh no! We've put an Integer into an ArrayList<String>!

That's obviously not typesafe. With wilcards, though, you can do this:

List<String> strings = new ArrayList<String>();
string.add("Hello");
List<? extends Object> objList = strings; // Works!
objList.add(new Integer(3)); // Compile-time error due to the wildcard restriction
dlev
  • 48,024
  • 5
  • 125
  • 132
  • It does strike me as a basic piece of inheritance functionality that should be built in to the normal syntax, but I'm sure they have their reasons for doing it this way. – Ankur Aug 06 '11 at 05:47
  • I've updated my answer to show what happens if you don't use wildcards. – dlev Aug 06 '11 at 05:48
  • 1
    It's a shame that we can't make edits which contains change of less than 6 characters – jimmy0251 Jun 18 '15 at 05:14
5

You can't do this because generics are not "covariant" i.e. a List<Integer> is not a sub-class of the List<Number> though Integer is a sub-class of Number.

Sanjay T. Sharma
  • 22,857
  • 4
  • 59
  • 71
  • But you can get co/contravariance to an extent with wildcards. – dlev Aug 06 '11 at 05:47
  • @dlev: AFAIK, no, that's not covariance because when using wildcards, you explicitly specify that `listTriples` can accept a `List` of any type which extends `SResource` including it. Compare and contrast this with Java arrays which show true covariance without the wildcards. – Sanjay T. Sharma Aug 06 '11 at 05:52
  • That's why I wrote "to an extent". And Java array covariance is generally considered broken, since they do not impose the "no writes" restriction, and so runtime checks to maintain type-safety are still necessary. – dlev Aug 06 '11 at 05:55
  • IMO it's not broken, just one of the decisions language designers had to make for practical purposes. If there were no writes restriction on arrays when Java came out, writing any sort of generic "array" handling method would have been a real pain. – Sanjay T. Sharma Aug 06 '11 at 06:04
  • Fair enough. I suppose "broken" is too strong; "not compile-time type-safe" is closer to reality. :) – dlev Aug 06 '11 at 06:06
  • You do get compile time type safety warnings though if you do things which are potentially unsafe.... usually catches most issues! – mikera May 23 '12 at 02:24
1

For those who are unable to add wildcards, this should work.

List<Integer> list = new ArrayList<Integer>();
new ArrayList<Number>(list);
Dan Grahn
  • 9,044
  • 4
  • 37
  • 74