假设我们有一个接口A
,由类B
实现:
public interface A {
public int getValueForName(String name);
}
public class B implements A {
public int getValueForName(String name) {
//implementation
}
public String getNameForValue(int value) {
//implementation
}
}
如果程序员在每次引用A
的实例时始终使用类型B
,那么在getNameForValue()
中定义但在B
中未指定的任何方法,例如A
{1}}被隐藏,任何持有A
类型引用的代码都无法访问。
这种方法似乎存在致命的缺陷。如何将引用B
(类型为A
)的实例的代码访问getNameForValue()
?
答案 0 :(得分:3)
真正的缺陷是接口的合同没有得到尊重。如果我使用界面,我不应该关心除界面中定义的任何其他方法。
如果接口没有定义我想要使用的方法,那么需要更新接口以适应新方法,或者我需要使用不同的接口。
例如,Collection
。如果我有一个绑定到Collection
合同的实例,那么我可以访问Iterator
。但是,我知道我回来的元素属于List
类型,而我想使用ListIterator
代替,这更强大。
问题在于 Collection
没有为我定义访问ListIterator
的任何方式。所以,我只有一个选项(因为我不能不必要地更新Collection
接口):改为使用List
接口。
如果您在自己的代码中遇到此场景,那么您的接口不支持它们需要的方法。在实例级别的界面级而非添加该支持。
答案 1 :(得分:1)
引用B实例(带有类型A)的代码如何访问getNameForValue()?
通过将实例强制转换为B。
接口的目的之一是定义两种或更多种类型共有的方法。这并不意味着您将为每种可能类型的每种可能组合定义接口方法,而只是为那些您希望通过接口公开的所有类型共有的方法。
因此,根据定义,拥有接口类型的实例会假定原始类型中可能存在您无法通过接口访问的方法。这就是它的工作原理。
您可能拥有界面的另一个原因是指定功能。因此,如果一个类是Iterable,那意味着它可以被迭代。如果我试图获得一个Iterable实例,我关心类中实现Iterable方法的方法,但我不关心任何其他方法,因为它们与Iterable功能没有任何关系。 / p>
简而言之,这是一个功能,而不是一个缺陷。
答案 2 :(得分:1)
从更现实的例子中看一下。如果我被蒙上眼睛,你告诉我在我面前有一只家庭宠物我该怎么办呢?可能是宠物,这将是全部。现在,如果你告诉我它是一只仓鼠,我也可以把它放在一个轮子里让它跑来跑去(另一方面我的猫不会那么想)。
当您将变量声明为接口(或更高级别的类)时,例如A
,它与上例中的家庭宠物相同。因此,并非所有方法都可供您使用。
要知道它是仓鼠还是B
你必须移除眼罩。对于Java而言,这意味着调用instanceof
,然后将变量转换为B
。您需要instanceof
以确保演员阵容是安全的。当然,除非您知道该内容是B
,否则您可能希望实际上将其声明为B
。
如果你发现自己正在投掷,你可能已经对你的设计做了一些“错误”。由于Java得到了泛型,因此不得不再进行投射。