【问题标题】:Java using generics with lists and interfacesJava 使用带有列表和接口的泛型
【发布时间】:2011-06-24 23:38:57
【问题描述】:

好的,这是我的问题:

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

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

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

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

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

【问题讨论】:

  • 如果不修改列表的定义,就没有干净的方法可以做到这一点。想象一下你做到了,那么这是对的,但不是:class Record2 extends Record { ... }; recordKeeper.myList = records; recordKeeper.myList.add(new Record2()); 你知道为什么错了吗?
  • 如果你使用'? extends Record' 那么你的意思是你不知道列表的确切类型,所以你必须使用类型转换(在列表上)来添加元素。 Java 会知道列表中的每个元素都扩展了 Record,因此您可以从中获取 Record 对象,但您不能插入任何 Record,因为 List 是使用 Java 不知道的类型键入的。你想参加聚会,但门卫把客人名单弄丢了。
  • 所以,八年后,我回顾这个问题,意识到 Java 编译器(曾经?有一段时间没用过 Java)只是愚蠢,并且不擅长 LSP。

标签: java generics parameters


【解决方案1】:

这行得通:

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

一个List&lt;Record&gt;只能包含Records(包括subRecords),一个List&lt;SubRecord&gt;只能包含SubRecords

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

List&lt;Record&gt;List&lt;SubRecord&gt; 都是 List&lt;? extends Record&gt;。但是在List&lt;? extends Record&gt; 中你不能添加任何东西,因为java 不知道List 是哪个确切类型的容器。想象一下你可以,那么你可以有以下语句:

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

正如我们刚刚看到的,这仅适用于List&lt;Record&gt;,而不适用于任何List&lt;Something that extends Record&gt;,例如List&lt;SubRecord&gt;

问候, 斯蒂芬

【讨论】:

  • 所以,碰巧有一个解决这个问题的棘手方法。 List a = (List)(List extends Record) b;通过双重转换子记录列表,实际上可以使用 a 引用 b。
  • 我怀疑你可以在运行时使用它添加记录。此外,似乎不需要第二次铸造(最右边的)。不,在你的解释中,说 List 是一个列表。反之亦然。
  • 实际上它在运行时确实有效。你把它弄反了。列表 extends Record> 是 List,因为任何扩展 Record 的东西都是 Record,因为扩展 Record 的东西包含 Record 的所有属性。
  • 好的。继承是一门专业。正确的 ?因此,当您说“扩展 Record 的所有内容都是 Record”时,这是真的。但是,如果您对容器所说的是真的:因为 List 可以保存记录(以下语句在 java 中有效: List.add(new Record() );),所以它应该是对于扩展它的类是正确的。而且您不能将记录添加到列表中 extends Record>,以下语句无效:List.add(新记录());。问候,Stéphane
【解决方案2】:

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

  • List&lt;Record&gt; 是一个列表,您可以在其中放置任何实现 Record 的对象,并且您取出的每个对象都将实现 Record
  • List&lt;SubRecord&gt; 是一个列表,您可以在其中放置任何实现 SubRecord 的对象,并且您取出的每个对象都将实现 SubRecord

如果允许简单地将List&lt;SubRecord&gt; 用作List&lt;Record&gt;,那么将允许以下内容:

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

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

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

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

  • 从一开始就使用List&lt;Record&gt;。 (您可以毫无问题地向其中添加 SubRecord 对象。)
    • 作为此方法的一种变体,您可以使用List&lt;? super Subrecord&gt; 作为添加内容的方法。 List&lt;Record&gt; 是 this 的子类型。
  • 复制列表:

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

扩展一下变体:

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


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

【讨论】:

  • 我要试试你的名单 super Subrecord> 解决方案,因为这可能正是我正在寻找的。我希望如此,因为我觉得虽然我目前的解决方案有效,但它有点 hack-ish。
  • 啊,可悲的是,我仍然找不到使用它的方法,这样它就可以完成与我的答案相同的事情。我寄予厚望。 :'(
  • 无法以类型安全的方式执行此操作。你可以欺骗编译器(如果你不使用特殊的typechecking collections,VM 不知道类型,它会在运行时工作),但它不是类型安全的。
【解决方案3】:

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

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

附带说明,我建议您阅读 oracle 网站上的此文档:http://download.oracle.com/javase/tutorial/java/generics/index.html 它很好地解释并深入研究了泛型。

【讨论】:

    【解决方案4】:

    这项工作,但你应该被警告!!!!

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

    您可以通过这种方式从对象列表转换为接口列表。 但是,如果您在代码中的某处获得此列表,并且尝试在此列表中添加一些内容,您会添加什么?该接口的接口或子类?您实际上丢失了列表类型的信息,因为您让它包含接口,因此您可以添加任何实现此接口的东西,但列表仅包含子类,如果您尝试执行操作,您很容易得到类转换异常就像添加或使用其他一些子类添加或获取此列表。解决方案是:将源列表的类型更改为list&lt;Interface&gt;而不是强制转换,然后你就可以走了:)

    【讨论】:

      【解决方案5】:

      没有办法做到类型安全。

      List&lt;SubInterface&gt; 不能添加任意的 Interfaces。毕竟是SubInterfaces 的列表。

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

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

      【讨论】:

      • 如果您不想添加,那为什么要在您的问题中写:“但是我无法将接口添加到列表中,只有子接口”?
      • @MirroredFate,我错了。我不明白。这里只有一个列表——new ArrayList&lt;SubRecord&gt;()。您希望有两个变量引用此列表,myListrecords,对吗?
      • @Snicolas 如果我将泛型添加到 List 使其变为 List,我不能再将接口添加到该列表中,我只能添加实现或扩展接口的东西。我不要那个。 @Mike Samuel 我对其进行了编辑以使其更清晰。是的,myList 和 records 应该引用同一个列表。
      • @MirroredFate,你希望能够做到myRecordKeeper.myList.add(anyRecord)
      • @MirroredFate,你必须给出代码来解释你如何使用你的泛型列表(超类型)
      【解决方案6】:

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

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

      据我了解,这很有效,因为它需要一些小步骤。 List&lt;SubRecord&gt;List&lt;? extends Record&gt;List&lt;? extends Record&gt;List&lt;Record&gt;。它可能不漂亮,但它仍然有效。

      【讨论】:

      • 不,List&lt;? extends Record&gt;List&lt;Record&gt;List&lt;Subrecord&gt; 作为子类型。编译器应该在这里给你一个警告。 is a 关系是错误的表示法。
      • 如果你开心就好。当您在其他地方更改程序时,请确保如果由于这种非类型安全的强制转换而在另一个点开始中断,您不会感到惊讶。
      猜你喜欢
      • 2021-10-03
      • 1970-01-01
      • 1970-01-01
      • 2021-09-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多