Java 立即向下投射的好处是什么?

Java 立即向下投射的好处是什么?,java,coding-style,Java,Coding Style,我最近一直在看很多代码(为了我自己的利益,因为我还在学习编程),我注意到一些Java项目(来自那些看起来很受尊敬的程序员),其中他们使用某种即时向下转换 我实际上有多个示例,但这里有一个是我直接从代码中提取的: public Set<Coordinates> neighboringCoordinates() { HashSet<Coordinates> neighbors = new HashSet<Coordinates>(); neighb

我最近一直在看很多代码(为了我自己的利益,因为我还在学习编程),我注意到一些Java项目(来自那些看起来很受尊敬的程序员),其中他们使用某种即时向下转换

我实际上有多个示例,但这里有一个是我直接从代码中提取的:

public Set<Coordinates> neighboringCoordinates() {
    HashSet<Coordinates> neighbors = new HashSet<Coordinates>();
    neighbors.add(getNorthWest());
    neighbors.add(getNorth());
    neighbors.add(getNorthEast());
    neighbors.add(getWest());
    neighbors.add(getEast());
    neighbors.add(getSouthEast());
    neighbors.add(getSouth());
    neighbors.add(getSouthWest());
    return neighbors;
}
公共集邻域坐标(){
HashSet邻居=新HashSet();
add(getNorthWest());
add(getNorth());
add(getNorthEast());
add(getWest());
add(getEast());
add(getSouthwest());
add(getSouth());
add(getsoutwest());
回归邻居;
}
在同一个项目中,这里有另一个(也许更简洁)的例子:

private Set liveCellCoordinates=new HashSet();
在第一个示例中,您可以看到该方法的返回类型为
Set
——但是,该特定方法始终只返回
HashSet
——而不返回其他类型的
Set

在第二个示例中,
liveCellCoordinates
最初定义为
,但立即变成
哈希集

不仅仅是这个单一的、具体的项目,我发现在多个项目中都是这样


我很好奇这背后的逻辑是什么?是否有一些代码约定会考虑这一良好的实践?它会使程序更快或更高效吗?它有什么好处?

因为现在如果他们将来更改类型,任何依赖于相邻坐标的代码都不必更新

让我们看看你有:

HashedSet<Coordinates> c = neighboringCoordinates()
HashedSet c=邻域坐标()
现在,让我们假设他们更改了代码以使用不同的set实现。你猜怎么着,你也得修改代码

但是,如果您有:

Set<Coordinates> c = neighboringCoordinates()
Set c=neightringcoordinates()
只要它们的集合仍然实现set,它们就可以在内部更改它们想要的任何内容,而不会影响您的代码


基本上,它只是为了隐藏内部细节而尽可能不具体(在合理范围内)。您的代码只关心它可以作为集合访问集合。它不在乎它是什么特定类型的集合,如果这有意义的话。因此,为什么要将代码耦合到HashedSet?

在设计方法签名时,通常最好只锁定需要锁定的内容。在第一个示例中,通过仅指定该方法返回
(而不是
哈希集
),如果发现
哈希集
不是正确的数据结构,则实现者可以自由更改实现。如果该方法已声明为返回一个
HashSet
,那么依赖于该对象的所有代码都是
HashSet
,而不是更一般的
Set
类型,也需要修改

一个现实的例子是,如果决定
neightringcoordinates()
需要返回线程安全的
Set
对象。如前所述,将方法的最后一行替换为:

return Collections.synchronizedSet(neighbors);
结果是,
synchronizedSet()
返回的
Set
对象与
HashSet
的赋值不兼容。幸好该方法被声明为返回一个

类似的考虑也适用于第二种情况。使用liveCellCoordinates的类中的代码不需要知道任何东西,只需要知道它是一个集合。(事实上,在第一个例子中,我希望看到:

Set<Coordinates> neighbors = new HashSet<Coordinates>();
Set neights=new HashSet();
在方法的顶部。)

这很简单

在您的示例中,该方法返回
Set
。从API设计师的角度来看,与返回
HashSet
相比,这有一个显著的优势


如果在某个时刻,程序员决定使用
superperformancesetfordirections
,那么他可以在不更改公共API的情况下执行,如果在第一个示例中新类扩展了
Set

,那么该方法始终只返回HashSet是该类用户不必知道的实现细节。这使开发人员可以在需要时使用不同的实现。

这里的设计原则是“总是更喜欢指定抽象类型”

Set
是抽象的;没有这样一个具体的类
Set
——它是一个接口,根据定义是抽象的。该方法的契约是返回一个
Set
——由开发人员选择返回哪种
Set

您还应该对字段执行此操作,例如:

private List<String> names = new ArrayList<String>;
私有列表名称=新的ArrayList;
不是

private ArrayList name=new ArrayList;
稍后,您可能希望更改为使用
LinkedList
——指定抽象类型允许您在不更改代码的情况下执行此操作(当然,初始化除外)。

诀窍是“将代码转换为接口”

其原因是,在99.9%的情况下,您只希望来自
HashSet/TreeSet/WhateverSet
的行为符合所有人实现的
Set
-接口。它使您的代码更简单,而您真正需要说
HashSet
的唯一原因是指定所需的集具有什么行为

正如您可能知道的,HashSet相对较快,但返回的项似乎是随机顺序的。TreeSet稍微慢一点,但按字母顺序返回项目。只要代码的行为像一个集合,它就不在乎

这使得代码更简单,更容易使用

注意,集合的典型选择是HashSet,Map是HashMap,List是ArrayList。如果你使用
private List<String> names = new ArrayList<String>;
private ArrayList<String> names = new ArrayList<String>;