【问题标题】:Java generics in List return type on a method inherited from multiple interfaces从多个接口继承的方法的 List 返回类型中的 Java 泛型
【发布时间】:2017-11-15 17:31:32
【问题描述】:

我目前在一家拥有多种模块的公司工作。在该公司中,如果您想提供模块内部,您可以通过 java 接口提供它,该接口隐藏实际的实现类型并为请求模块提供接口。现在我希望有一个提供者能够为多个模块提供数据,这些模块公开实际内部数据的不同字段或方法。

因此,我有一个内部对象,其中包含一些数据,并且每个模块都有一个接口,需要访问一些但不是严格来说所有字段。最后,我有一个外部对象,它实现了所有这些接口,并拥有一个内部对象的实例来委托方法调用:

public class InternalObject {
    public int getA() { return 0; }
    public int getB() { return 0; }
}

public interface ModuleXObject {
   int getA();
}

public interface ModuleYObject {
    int getA();
    int getB();
}

public class ExternalObject implements ModuleXObject, ModuleYObject {
    private InternalObject _internal;

    public int getA() { return _internal.getA(); }
    public int getB() { return _internal.getB(); }
}

现在这一切都很好,但如果我想提供 - 让我们说 - 用于查找为正确模块键入的所述对象列表的存储库方法,我会遇到如何实现这一点的问题。我希望得到以下内容:

public interface ModuleXObjectRepository {
    List<ModuleXObject> loadAllObjects();
}

public interface ModuleYObjectRepository {
    List<ModuleYObject> loadAllObjects();
}

public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
    public List<ExternalObject> loadAllObjects() {
        // ...
    }
}

这不会编译,说返回类型不兼容。 所以我的问题是,是否有可能实现这样的目标,如果,如何实现?


我应该指出,我尝试了一些不同的方法,我想将它们包括在内以保持完整性并描绘它们的缺点(在我看来)。

方法一

public interface ModuleXObjectRepository {
    List<? extends ModuleXObject> loadAllObjects();
}

public interface ModuleYObjectRepository {
    List<? extends ModuleYObject> loadAllObjects();
}

public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
    public List<ExternalObject> loadAllObjects() {
        // ...
    }
}

这种方法非常接近我想要的解决方案,但会产生如下代码:

List<? extends ModuleXObject> objects = repository.loadAllObjects();

因此要求用户在每个 List-Declaration 中包含关于 loadAllObjects() 调用的“? extends”。

方法2

public interface ModuleXObjectRepository {
    List<ModuleXObject> loadAllObjects();
}

public interface ModuleYObjectRepository {
    List<ModuleYObject> loadAllObjects();
}

public class ExternalObjectRepository implements ModuleXObjectRepository, ModuleYObjectRepository {
    public List loadAllObjects() {
        // ...
    }
}

这种方法只是省略了 ExternalObjectRepository 中的泛型,因此在我看来过多地降低了类型安全性。另外我还没有测试这是否真的有效。


只是为了重新练习,是否有任何可能的方法来定义 loadAllObjects 方法,使用户能够在没有

的情况下获取使用其各自模块的对象键入的列表
  • 在用户代码中需要“?extends”
  • 降低存储库实现中的类型安全性
  • 使用类/接口级别的泛型

【问题讨论】:

  • 嗨,泰勒,感谢您的评论!事实上,我已经考虑过以某种方式使用“超级”,但我想不出哪个是确切的解决方案。至少&lt;T super ModuleXObject &amp; ModuleYObject&gt; List&lt;T&gt; loadAllObjects() 不起作用。你有更好的主意吗?
  • 我将添加我的详细答案。给我几个

标签: java generics


【解决方案1】:

允许将其键入为List&lt;ModuleXObject&gt; 的挑战在于,其他代码可能包含List&lt;ExternalObject&gt;

所有ExternalObject 实例都是ModuleXObject 实例,但反之则不然。

考虑以下附加类:

public class MonkeyWrench implements ModuleXObject{
    //STUFF
}

MonkeyWrench 实例不是ExternalObject 实例,但如果可以将List&lt;ExternalObject&gt; 转换为List&lt;ModuleXObject&gt;,则可以将MonkeyWrench 实例添加到此集合中,这会导致运行时类转换异常的风险和废墟类型安全。

其他代码很容易有:

for(ExternalObject externalObject:externalObjectRepository.loadAllObjects())

如果其中一个实例是 MonkeyWrench 实例,则运行时类转换,这是泛型应避免的。

? extends ModuleXObject 的含义是您可以将集合中的任何对象作为ModuleXObject 读取,但您不能将任何内容添加到集合中,因为其他代码可能对集合具有不明显/可用的附加约束在编译时。

我建议在您的情况下使用? extends ModuleXObject,因为它的语义似乎与您想要的一致,即提取ModuleXObject 实例,例如

ModuleXObjectRepository repo = //get repo however
for(ModuleXObject obj : repo.loadAllObjects()){
    //do stuff with obj
}

【讨论】:

  • 再次感谢您的回答。因此,如果我正确理解这一点,则可能没有满足我要求的所有约束的问题的解决方案。但是最干净的解决方案是什么?由于我不需要修改加载的列表(添加或从中删除),它可能是用户代码中带有“?扩展”的方法 1。另一方面,我没有 ModuleX/YObject 的其他实现,但这就是它目前的样子。无论如何,放弃类型安全可能是更肮脏的解决方案。
  • 我会使用? extends Whatever 方法。这是库中非常常见的模式,并且具有清晰的语义。至少在我的经验中,图书馆消费者处理这种情况并不少见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-26
  • 1970-01-01
  • 1970-01-01
  • 2014-03-05
  • 2012-11-12
  • 1970-01-01
相关资源
最近更新 更多