Java 窄型还是宽型?在我的公共API中返回什么
最近,我和我的朋友讨论了创建公共API的良好实践,我们在返回类型方面开始争论。他告诉我只返回扩展的eg类型列表,而不是ArrayList。但在我看来,这一切都取决于我们试图解决的问题。 我看不出有什么不对:Java 窄型还是宽型?在我的公共API中返回什么,java,oop,Java,Oop,最近,我和我的朋友讨论了创建公共API的良好实践,我们在返回类型方面开始争论。他告诉我只返回扩展的eg类型列表,而不是ArrayList。但在我看来,这一切都取决于我们试图解决的问题。 我看不出有什么不对: ArrayList foo(); ArrayList foo() 当我想说“嘿,我给你ArrayList只是为了确保你能在O(1)时间内访问元素” 在下一个示例中,让我们假设在方法签名或文档中没有关于返回什么类型的图或使用什么表示的信息 图foo() 现在怎么办?我可能想知道这个图是有向的
ArrayList foo();
ArrayList foo()
当我想说“嘿,我给你ArrayList只是为了确保你能在O(1)时间内访问元素”
在下一个示例中,让我们假设在方法签名或文档中没有关于返回什么类型的图或使用什么表示的信息
图foo()
现在怎么办?我可能想知道这个图是有向的还是无向的,基于邻接列表还是邻接矩阵。最糟糕的情况是,它可能导致以下代码:
Graph graph = foo();
if(graph instanceof DirectedGraph)
//code
这违反了Liskov替换原则
你的想法是什么?想想你为什么要公开一个API,为什么要有人使用它? 最明显的原因是对API的用户隐藏问题(和解决方案)的复杂性 API的用户不想关心实现细节和您从他那里抽象出来的潜在复杂性 作为一个API提供者,一般来说,您不希望将自己局限于具体的实现,例如本例中的
ArrayList
。即使您应该在大多数情况下使用ArrayList
作为List
接口实现,也不可能让自己处于LinkedList
性能更好的情况
这将破坏您现有的API,客户端将不得不重新实现更改。这正是你不想做的
看一看这个概念
编辑
是否可以将有向图和一个图放在同一个接口后面取决于您的用例。但是,您可以做的是提供两个接口,一个用于graph,另一个用于directed graph,以保护API用户不受这些图形实现中的未来更改的影响
API的用户应该能够指定他想要的是哪种图形,是定向的还是非定向的,并且您应该能够向他隐藏具体的图形实现细节
当我想说“嘿,我给你ArrayList只是为了保证
你在O(1)时间内访问了元素”
听起来像是过早的优化
图foo()
图总是隐式无向的。有向图是定向的。我觉得将有向图称为图是一种滥用术语,除非你已经明确区分了无向图和有向图——在这种情况下,我会将IGraph作为接口,然后让有向图和图实现该接口
从使用graph作为数据类型(无行为)的角度来看,我认为使用有向图作为图形没有问题——它只会忽略其元素(边和顶点)的顺序。但是,使用图作为有向图可能会有问题,因为元素的顺序可能是随机的(如果它的元素存储在一个有向图中)。也就是说,您不能确保元素总是以相同的顺序返回,这不会使它们的顺序保持不变
当我想说“嘿,我给你ArrayList只是为了确保你能在O(1)时间内访问元素”
您必须在接口中只公开所需的内容,不能多也不能少。因此,如果您需要保证O(1)的访问,那么使用ArrayList是正确的,您的实现不太可能因此而改变
图foo()
这个实现告诉您这两种图都可以返回,所以这取决于您根据需要对其进行强制转换或检查
不要做早期优化
我看不出有什么不对:
ArrayList foo();
当我想说“嘿,我给你ArrayList只是为了确保你能在O(1)时间内访问元素”
在返回ArrayList时,这并不是您要说的唯一一句话。您的意思是:这个方法返回List:ArrayList的一个非常具体的实现,这个方法的所有重写也必须返回这个非常具体的实现,而不是其他任何东西
列表的其他几个实现是O(1)而不是ArrayList:
- Arrays.asList()返回的列表
- CopyOnWriteArrayList
- Collections.emptyList()或Collections.singletonList()返回的列表
- 包装到Collections.synchronizedList()或Collections.unmodifiableList()中的任何O(1)列表
当然,这实际上取决于您所使用的类型、它们的设计和用例。设计与其说是一门科学,不如说是一门艺术。回答得很简洁。我要补充的是,有时Java泛型在这样的情况下很好(但显然取决于域/上下文)。