【问题标题】:Java generic type inheritance, List element type inferenceJava泛型类型继承、List元素类型推断
【发布时间】:2021-08-22 05:41:37
【问题描述】:

我有以下单级继承。我无法理解的一件事是,getLine() 被推断为BaseLine,因为BaseDocument<T extends BaseLine> 而没有强制转换。但是对于列表,这不适用,每个元素都返回为Object。我的问题是:

  1. 为什么编译器不能将元素类型推断为BaseLine

  2. 如何更改实现,以便在迭代时获取元素为 BaseLine

     public void test(){
         BaseDocument baseDocument = new Document();
         for (Object orderLine : baseDocument.getOrderLines()) {
    
         }
    
         BaseLine line = baseDocument.getLine();
     }
    
    
     static class Document extends BaseDocument<Line> {
         @Override
         public List<Line> getOrderLines() {
             return null;
         }
    
         @Override
         public Line getLine() {
             return null;
         }
     }
    
     static abstract class BaseDocument<T extends BaseLine> {
         public abstract List<T> getOrderLines();
         public abstract T getLine();
     }
    
    
     static class Line extends BaseLine {
     }
    
     static abstract class BaseLine {
     }
    

【问题讨论】:

    标签: java generics inheritance type-inference


    【解决方案1】:

    当你有:

    BaseDocument baseDocument = new Document();
    

    您正在使用 原始类型。原始类型会删除类中的所有通用信息。这包括其他泛型类型。所以有了上面的内容,就好像这个类是这样写的:

    static abstract class BaseDocument {
    
      public abstract List getOrderLines();
    
      public abstract BaseLine getLine();
    }
    

    List 的类型参数隐含地由Object 限定,因此擦除为Object

    你不应该使用原始类型,除非你被遗留代码(在 Java 5 之前编写)强制使用。如果您不想给出实际类型,请使用通配符:

    BaseDocument<?> baseDocument = new Document();
    for (BaseLine orderLine : baseDocument.getOrderLines()) {
      // do something...
    }
    

    您可以保留BaseDocument 的实现原样。

    【讨论】:

      【解决方案2】:

      编辑:在我看来,这个答案更不安全。检查@Slaw 的答案以获得更好的解释。正如作者所解释的,它也有缺陷。选择答案时要注意复杂性

      原因在这一行BaseDocument baseDocument = new Document(); BaseDocumet&lt;T extends BaseLine&gt; 是泛型类型,需要声明其类型,否则无法推断 T 的类型,因此它会自动解析为 Object

      解决问题的一种方法是将baseDocument.getOrderLines() 转换为List&lt;BaseLine&gt;

      List&lt;BaseLine&gt; lines = baseDocument.getOrderLines(); 这将引发uncheked cast 警告,但项目将编译

      【讨论】:

      • 这只是我突然想到的(对不起,错误的信息):实际上,我所说的关于您的解决方案的问题,可以将错误的类型添加到列表中?在我的回答中使用通配符方法时,这仍然是可能的。我能想到的两种防止这种情况的方法是返回List&lt;? extends T&gt; 或简单地返回一个不可修改的列表(以防止从外部修改)。无论如何,使用通配符而不是原始类型肯定更好。
      • 老实说,我知道“使用通配符时仍会添加错误的类型”。但我想不出任何更好的方法来解决我的情况。我的具体类Document 需要返回BaseLine 的具体实现。我不能让我的列表不可变,因为其他地方应该能够在某些条件下修改该列表,所以这也不是我的选择。
      • 天哪……我很抱歉。当我想到这个想法时,我在发布我之前的评论之前检查了我的 IDE,它没有给出任何错误。它一定是在更新或索引什么的,因为现在它确实给出了一个错误。执行baseDocument.getOrderLines().add(new SomeOtherLine()) 会产生incompatible types: SomeOtherLine cannot be converted to CAP#1 错误。当然,仍然有一些方法可以“击败”编译时泛型类型检查,但也许我的解决方案没有我想象的那么严重的漏洞。
      • 好吧,谢谢你。我刚刚回忆起,您不能向定义为List&lt;? extends T&gt; 的列表添加内容,因为该列表可以是扩展T 的任何类型。我在这里也犯了一个错误“我知道“使用通配符时仍然可以添加错误的类型”。”。谢谢你的思考,真的很感激你在做什么
      猜你喜欢
      • 2015-07-04
      • 1970-01-01
      • 2020-12-01
      • 2021-10-11
      • 1970-01-01
      • 1970-01-01
      • 2018-04-01
      • 2021-10-21
      • 1970-01-01
      相关资源
      最近更新 更多