繁体   English   中英

Java使用带有列表和接口的泛型

[英]Java using generics with lists and interfaces

好的,所以这是我的问题:

我有一个包含接口的列表 - List<Interface> a - 以及扩展该接口的接口List<SubInterface> bList<SubInterface> b 我想设置a = b 我不希望使用addAll()或任何会花费更多内存的东西,因为我正在做的事情已经非常耗费成本。 我真的需要能够说a = b 我试过List<? extends Interface> a List<? extends Interface> a ,但是我不能将接口添加到列表a ,只能添加SubInterfaces。 有什么建议?

我希望能够做到这样的事情:

List<SubRecord> records = new ArrayList<SubRecord>();
//add things to records
recordKeeper.myList = records;

RecordKeeper类是包含接口列表(NOT子接口)的类

public class RecordKeeper{
    public List<Record> myList;
}

这有效:

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

    }

}

我的意思是它编译,但是你必须要投你List<? extends Record> 在添加任何内容之前List<? extends Record> 如果要转换为的类型是Record的子类,Java将允许转换,但它无法猜测它将是哪种类型,您必须指定它。

List<Record>只能包含Records (包括subRecords ), List<SubRecord>只能包含SubRecords

但是A List<SubRecord >不是List<Record> ,它不能包含Records ,而子类应该总是做超类可以做的事情。 这很重要,因为继承是specilisation,如果List<SubRecords>将是List<Record>的子类,它应该能够包含`但它不是。

List<Record>List<SubRecord>都是List<? extends Record> List<? extends Record> 但是在List<? extends Record> List<? extends Record>你不能添加任何东西,因为java无法知道List是哪个精确类型的容器。 想象一下你可以,然后你可以有以下声明:

List<? extends Record> l = l2;
l.add( new Record() );

正如我们刚刚看到的那样,这只能用于List<Record>而不能用于任何List<Something that extends Record>例如List<SubRecord>

此致,Stéphane

只是为了解释为什么Java不允许这样做:

  • List<Record>是一个列表,您可以在其中放置任何实现Record对象,并且您获得的每个对象都将实现Record
  • List<SubRecord>是一个列表,您可以在其中放置任何实现SubRecord对象,并且您获得的每个对象都将实现SubRecord

如果允许简单地使用List<SubRecord>作为List<Record> ,则允许以下内容:

List<SubRecord> subrecords = new ArrayList<SubRecord>();
List<Record> records = subrecords;
records.add(new Record()); // no problem here

SubRecord sr = subrecords.get(0); // bang!

你看,这不是类型安全的。 List(或参数化类的任何opject,实际上)不能类型安全地更改其参数类型。

在您的情况下,我看到这些解决方案:

  • 从头开始使用List<Record> (您可以SubRecord地将SubRecord对象添加SubRecord 。)
    • 作为此变体,您可以使用List<? super Subrecord> List<? super Subrecord>用于添加内容的方法。 List<Record>是此类型的子类型。
  • 复制清单:

     List<Record> records = new ArrayList<Record>(subrecords); 

稍微考虑一下变化:

void addSubrecords(List<? super Subrecord> subrecords) {
    ...
}


List<Record> records = new ArrayList<Record>();
addSubrecords(records);
recordkeeper.records = records;

这个工作,但你应该警告!!!!

@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(); 
}

您可以通过这种方式从对象列表转换为接口列表。 但是,如果您在代码中的某个位置获得此列表,并且如果您尝试向此列表添加内容,那么您会添加什么? 这个接口的Inteface或Subclass? 你实际上是松散了列表类型的信息,因为你让它保持接口,所以你可以添加任何实现这个接口的东西,但是列表只保存子类,如果你尝试进行操作,你很容易得到类强制转换异常喜欢使用其他子类添加或获取此列表。 解决方案是:更改源列表的类型list<Interface>而不是cast,然后你可以自由地去:)

你不能这样做并且安全,因为List<Interface>List<SubInterface>是Java中的不同类型。 即使您可以将SubInterface类型添加到Interface列表中,也不能将这两个列表等同于不同的接口,即使它们是彼此的子/超级接口。

你为什么要这么做b =这么糟糕? 您是否只想存储SubInterface列表的引用?

另外,我建议你在oracle网站上阅读这篇文档: http//download.oracle.com/javase/tutorial/java/generics/index.html它解释并深入了解泛型。

没有办法做类型安全的。

List<SubInterface>不能添加任意Interface 毕竟它是一个SubInterface列表。

如果您确信这样做是安全的,即使它不是类型安全的,您也可以这样做

@SuppressWarnings("unchecked")
void yourMethodName() {
  ...
  List<Interface> a = (List<Interface>) b;
  ...
}

所以,我的朋友找到的相当简单的解决方案是这样的:

recordKeeper.myList = (List<Record>)(List<? extends Record>)records;

这是我理解的,因为它需要婴儿步骤。 List<SubRecord>是一个List<? extends Record> List<? extends Record> ,并List<? extends Record> List<? extends Record>List<Record> 它可能不漂亮,但它仍然有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM