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

时间:2011-06-24 23:38:57

标签: java generics parameters

好的,这是我的问题:

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

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

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

类RecordKeeper是包含接口列表的那个(非子接口)

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

6 个答案:

答案 0 :(得分:6)

这有效:

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>。如果要转换为的类型是Record的子类,Java将允许转换,但它无法猜测它将是哪种类型,您必须指定它。

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

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

List<Record>List<SubRecord>都是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>

此致  斯特凡

答案 1 :(得分:2)

只是解释为什么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个对象。)
    • 作为其变体,您可以使用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;

答案 2 :(得分:1)

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

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

另外,我建议您在oracle网站上阅读此文档:http://download.oracle.com/javase/tutorial/java/generics/index.html 它很好地解释了泛型并深入到泛型中。

答案 3 :(得分:0)

没有办法做类型安全的事。

List<SubInterface>无法添加任意Interface个。毕竟这是SubInterface的列表。

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

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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

@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>而不是强制转换,然后您可以自由选择:)