Java using generics with lists and interfaces

后端 未结 6 1744
遇见更好的自我
遇见更好的自我 2021-01-13 11:02

Ok, so here is my problem:

I have a list containing interfaces - List a - and a list of interfaces that extend that interface: Li

相关标签:
6条回答
  • 2021-01-13 11:41

    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 :)

    0 讨论(0)
  • 2021-01-13 11:49

    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

    0 讨论(0)
  • 2021-01-13 11:50

    Just to explain why Java does not permit this:

    • A List<Record> is a list in which you can put any object implementing Record, and every object you get out will implement Record.
    • A 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:

    • Use List<Record> from start. (You can add SubRecord objects to this without problems.)
      • as a variation of this, you can use 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;
    
    0 讨论(0)
  • 2021-01-13 11:51

    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.

    0 讨论(0)
  • 2021-01-13 11:52

    There's no way to do it that is type safe.

    A List<SubInterface> cannot have arbitrary Interfaces added to it. It is a list of SubInterfaces 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;
      ...
    }
    
    0 讨论(0)
  • 2021-01-13 12:06

    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.

    0 讨论(0)
提交回复
热议问题