Ok, so here is my problem:
I have a list containing interfaces - List
- and a list of interfaces that extend that interface: Li
THIS WORK, BUT YOU SHOULD BE WARN !!!!
@Override
// Patient is Interface
public List<Patient> getAllPatients() {
// patientService.loadPatients() returns a list of subclasess of Interface Patient
return (List<Patient>)(List<? extends Patient>)patientService.loadPatients();
}
You can cast from List of Objects to List of Interface in this way.
But, if you get this list somewhere in your code and if you try to add something to this list, what would you add? Inteface or Subclass of this interface ? You actually loose information of the type of list, because you let it hold Interface, so you can add anything that implement this interface, but the list is holding the subclasses only, and you could easily get class cast exception if you try to do operations like add or get on this list with some other subclass. The solution is: Change The type of source list to list<Interface>
instead of cast, then you are free to go :)
This works :
public class TestList {
interface Record {}
interface SubRecord extends Record {}
public static void main(String[] args) {
List<? extends Record> l = new ArrayList<Record>();
List<SubRecord> l2 = new ArrayList<SubRecord>();
Record i = new Record(){};
SubRecord j = new SubRecord(){};
l = l2;
Record a = l.get( 0 );
((List<Record>)l).add( i ); //<--will fail at run time,see below
((List<SubRecord>)l).add( j ); //<--will be ok at run time
}
}
I mean it compiles, but you will have to cast your List<? extends Record>
before adding anything inside. Java will allow casting if the type you want to cast to is a subclass of Record
, but it can't guess which type it will be, you have to specify it.
A List<Record>
can only contain Records
(including subRecords
), A List<SubRecord>
can only contain SubRecords
.
But A List<SubRecord
> is not a List<Record>
has it cannot contains Records
, and subclasses should always do what super classes can do. This is important as inheritance is specilisation, if List<SubRecords>
would be a subclass of List<Record>
, it should be able to contain ` but it'S not.
A List<Record>
and a List<SubRecord>
both are List<? extends Record>
. But in a List<? extends Record>
you can't add anything as java can't know which exact type the List
is a container of. Imagine you could, then you could have the following statements :
List<? extends Record> l = l2;
l.add( new Record() );
As we just saw, this is only possible for List<Record>
not for any List<Something that extends Record>
such as List<SubRecord>
.
Regards, Stéphane
Just to explain why Java does not permit this:
List<Record>
is a list in which you can put any object implementing Record
, and every object you get out will implement Record
.List<SubRecord>
is a list in which you can put any object implementing SubRecord
, and every object you get out will implement SubRecord
.If it would be allowed to simply use a List<SubRecord>
as a List<Record>
, then the following would be allowed:
List<SubRecord> subrecords = new ArrayList<SubRecord>();
List<Record> records = subrecords;
records.add(new Record()); // no problem here
SubRecord sr = subrecords.get(0); // bang!
You see, this would not be typesafe. A List (or any opject of a parametrized class, in fact) can not typesafely change its parameter type.
In your case, I see these solutions:
List<Record>
from start. (You can add SubRecord
objects to this without problems.)
List<? super Subrecord>
for the method which adds stuff. List<Record>
is a subtype of this.copy the list:
List<Record> records = new ArrayList<Record>(subrecords);
To exand a bit on th variation:
void addSubrecords(List<? super Subrecord> subrecords) {
...
}
List<Record> records = new ArrayList<Record>();
addSubrecords(records);
recordkeeper.records = records;
So, the rather simple solution a friend of mine found was this:
recordKeeper.myList = (List<Record>)(List<? extends Record>)records;
This works as I understand it because it takes baby steps. List<SubRecord>
is a List<? extends Record>
, and List<? extends Record>
is a List<Record>
. It might not be pretty, but it works nonetheless.
There's no way to do it that is type safe.
A List<SubInterface>
cannot have arbitrary Interface
s added to it. It is a list of SubInterface
s after all.
If you are convinced that this is safe to do even though it is not type safe you can do
@SuppressWarnings("unchecked")
void yourMethodName() {
...
List<Interface> a = (List<Interface>) b;
...
}
You can't do that and be safe because List<Interface>
and List<SubInterface>
are different types in Java. Even though you can add types of SubInterface to a list of Interface, you can't equate the two lists with different interfaces even if they're sub/super interfaces of eachother.
Why is it that you want to do b = a so bad? Do you just want to store a reference to the SubInterface list?
On a side note, I suggest you read this documentation on the oracle site: http://download.oracle.com/javase/tutorial/java/generics/index.html It explains and goes deep into generics very well.