2

假设我正在使用一个我不知道源代码的库。它有一个返回 List 的方法,如下所示:

public List<SomeObj> getObjs() { ... }

我想知道这是否是个好主意:

ArrayList<SomeObj> objs = (ArrayList<SomeObj>) getObjs();

例如,如果 getObjs() 中 List 的具体实现是 aLinkedList那么不会存在某种类型的差异吗?

4

9 回答 9

6

不,这不是一个好主意。您应该始终使用 interface( List) 来声明您的列表变量,除非您出于某种原因需要来自ArrayList.
此外,如果您这样做,您需要确定返回的列表是一个ArrayList. 在这种情况下,承诺的契约getObjs()只是返回类型是某种List,所以你不应该假设其他任何东西。即使List现在返回的是ArrayList,也没有什么可以阻止实现者getObjs()稍后更改List返回的类型,这会破坏您的代码。

于 2012-08-08T14:53:27.677 回答
4

转换为您正在调用的 API 定义的返回类型。

如果它说它返回 a List<SomeObj>,那么就是这样。不要尝试检索基础类型,因为:

a) 它可能随新版本而变化(甚至在调用之间)

b) 你不需要知道任何东西的实现类。

如果你需要一个 ArrayList,那么做new ArrayList<SomeObject)(getObjs());

于 2012-08-08T14:55:10.127 回答
3

使用接口(列表)的全部意义在于隐藏实现细节。为什么要将其转换为特定的实现?

于 2012-08-08T14:54:04.263 回答
2

你是对的。这不是一个好主意。您需要使用它返回的界面表单。

于 2012-08-08T14:53:38.303 回答
1

如果你向下转换为 ArrayList 而实际上它是一个 LinkedList,它会抛出一个 ClassCastException。一般来说,像这样向下转换从来都不是一个好主意,特别是如果您没有编写返回要向下转换的对象的代码。另外,如果您使用这样的 3rd 方库,他们可能会在改进代码时更改返回给您的内容。如果您在代码中进行这样的向下转换,它今天可能会起作用,但是当您升级库时,它会突然中断。它在运行时而不是编译时中断,所以在你运行它之前你不会知道它的损坏。这是您违反图书馆与您签订的合同的问题,该合同仅使用 List 接口。

于 2012-08-08T14:54:33.873 回答
1

它返回 a的原因List是您不必关心该接口的底层是什么。

List接口仅声明对象必须满足的契约,以及您如何查询它。图书馆作者将来可以自由选择一个ArrayList,a LinkedList,或者也许是a LazyDatabasePopulatedList。事实上,根据提供类的实现方式,您可能会在运行时获得不同的实现。

只要您有要遵守的合同,这就会为您带来很多自由。仅仅谈论和提供接口,以及尽可能少地处理具体的类,有很多话要说。

于 2012-08-08T14:56:11.393 回答
1

您应该始终使用界面代替Handle. 这就是OOPLanguages 的重点,以便您以后可以在任何实现之间切换。

如果您正在转换为任何具体类,编译器将警告您Cast可能发生异常的可能性。SuppressWarning如果您非常确定类型,则可以使用。

于 2012-08-08T14:57:10.983 回答
1

这不是一个好主意,因为您不知道该方法返回什么实现;如果它不是一个ArrayList,你会得到一个ClassCastException。事实上,您不应该关心该方法返回的确切实现。请改用List界面。

如果出于某种原因,您绝对需要一个ArrayList,然后创建您自己的并使用该方法返回的ArrayList初始化它:List

ArrayList<SomeObj> myOwnList = new ArrayList(getObjs());

但是不要这样做,除非你绝对需要ArrayList- 因为它效率低下。

于 2012-08-08T14:58:17.270 回答
1

你为什么不这样做:

List<SomeObj> objs = getObjs();

然后使用该列表。如果由于某种原因你真的需要一个 ArrayList,你总是可以这样做

ArrayList<SomeObj> arrayList = new ArrayList<SomeObj>();
arrayList.addAll(objs);

并与之合作。虽然这不是很有效。

于 2012-08-08T14:59:41.857 回答