据我了解,如果您使用Java实现接口,则实现该接口的子类必须使用该接口中指定的方法。
我注意到在某些接口(例如Collection接口)中,有一些方法被注释为可选方法,但这到底是什么意思?我觉得接口中指定的所有方法都需要吗?
据我了解,如果您使用Java实现接口,则实现该接口的子类必须使用该接口中指定的方法。
我注意到在某些接口(例如Collection接口)中,有一些方法被注释为可选方法,但这到底是什么意思?我觉得接口中指定的所有方法都需要吗?
Answers:
这里的答案似乎有很多混乱。
Java语言要求接口中的每个方法都由该接口的每个实现来实现。期。这条规定没有例外。说“收藏是一个例外”,这表明对这里的实际情况非常模糊。
重要的是要认识到符合接口有两个层次:
Java语言可以检查的内容。这几乎可以归结为:每个方法都有一些实现吗?
实际履行合同。也就是说,实现是否执行界面中的文档所述的内容?
编写良好的界面将包括文档,这些文档确切解释了实现的期望。您的编译器无法为您检查。您需要阅读文档,然后按他们说的做。如果您没有按照合同的约定执行操作,那么就编译器而言,您将拥有该接口的实现,但这将是有缺陷/无效的实现。
在设计Collections API时,Joshua Bloch决定,除了拥有非常细粒度的接口来区分不同的collection变体(例如:可读,可写,随机访问等)之外,他只会拥有非常粗糙的接口集,主要是
Collection
,List
,Set
和Map
,然后记录某些操作为“可选”。这是为了避免细粒度接口导致的组合爆炸。来自Java Collections API设计常见问题解答:
为了详细说明问题,假设您想在层次结构中添加可修改性的概念。您需要四个新接口:ModifiableCollection,ModifiableSet,ModifiableList和ModifiableMap。以前简单的层次结构现在变成了混乱的层次结构。另外,您需要一个新的Iterator接口以用于不可修改的Collection,其中不包含remove操作。现在,您可以取消UnsupportedOperationException吗?不幸的是没有。
考虑数组。它们执行大多数List操作,但不执行删除和添加操作。它们是“固定大小”列表。如果要在层次结构中捕获此概念,则必须添加两个新接口:VariableSizeList和VariableSizeMap。您不必添加VariableSizeCollection和VariableSizeSet,因为它们与ModifiableCollection和ModifiableSet相同,但是出于一致性考虑,您可以选择添加它们。此外,您还需要各种不支持添加和删除操作的ListIterator以及无法修改的List。现在,我们有多达十个或十二个接口,外加两个新的Iterator接口,而不是原来的四个。我们完了吗?没有。
考虑日志(例如错误日志,审核日志和可恢复数据对象的日志)。它们是自然的仅追加序列,支持所有列表操作,除去和设置(替换)除外。他们需要一个新的核心接口和一个新的迭代器。
与不可变集合相比,不可变集合又如何呢?(即,客户端无法更改的集合,并且由于其他任何原因也不会更改)。许多人认为这是最重要的区别,因为它允许多个线程同时访问集合而无需同步。将这种支持添加到类型层次结构中还需要四个接口。
现在我们有多达二十个左右的接口和五个迭代器,并且几乎可以肯定的是,实际上仍然有一些集合不能完全适合任何一个接口。例如,Map返回的集合视图是自然的仅删除集合。另外,有些集合会根据其值拒绝某些元素,因此我们仍然没有消除运行时异常。
说完所有内容后,我们认为通过提供很少的一组核心接口(可能会抛出运行时异常)来回避整个问题,这是合理的工程折衷方案。
当Collections API中的方法被记录为“可选操作”时,这并不意味着您可以只将方法实现留在实现中,也不意味着您可以使用空的方法主体(一方面,很多他们需要返回结果)。相反,这意味着一个有效的实现选择(仍然符合合同的选择)是抛出UnsupportedOperationException
。
注意,因为UnsupportedOperationException
是a RuntimeException
,就编译器而言,您可以将其从任何方法实现中抛出。例如,您可以从实现中抛出它Collection.size()
。但是,这样的实现会违反合同,因为的文档Collection.size()
没有说这是允许的。
另外:Java的Collections API使用的方法颇有争议(但是现在可能比第一次引入时要少)。在理想情况下,接口将没有可选操作,而将使用细粒度的接口。问题在于Java不支持推断的结构类型或交集类型,这就是为什么尝试以“正确的方式”做事最终变得非常笨拙的原因。
There are no exceptions to this rule
。想知道为什么这个答案没有被标记为接受。其他人很好,但您付出的远远不够。
Foo
未Runnable
使用public方法实现的对象void run()
。现在,创建一个类Bar
是extends Foo
和implements Runnable
无overiding run
。它仍然实现该方法,尽管是间接的。同样,默认方法实现仍然是实现。
remove
中提供了默认实现。如果您不实现它,那么您的类将获得默认实现。您提到的其他两种方法没有默认实现。
为了编译接口的实现类(非抽象类),必须实现所有方法。
但是,如果我们认为某个方法的实现是一个简单的异常抛出(如Collection
接口中的某些方法),则抛出的异常是“未实现” ,那么Collection
在这种情况下,接口就是例外,而不是常规情况。通常,实现类应该(并且将)实现所有方法。
集合中的“可选”意味着实现类不必“实现”(根据上面的术语),而只需抛出 NotSupportedException
)。
一个很好的例子- add()
不可变集合的方法-具体将只实现一个除了抛出以外什么都不做的方法NotSupportedException
如果这样Collection
做是为了防止混乱的继承树,那将使程序员感到痛苦-但在大多数情况下,不建议使用此范例,应尽可能避免。
更新:
从Java 8开始,引入了默认方法。
这意味着接口可以定义一种方法-包括其实现。
添加此代码是为了允许向接口添加功能,同时仍支持不需要新功能的代码段的向后兼容性。
请注意,该方法仍由声明该方法的所有类实现,但使用接口的定义。
Java中的接口只是声明实现类的协定。该接口中的所有方法都必须实现,但是实现类可以自由地将它们保留为未实现状态,即空白。举一个人为的例子
interface Foo {
void doSomething();
void doSomethingElse();
}
class MyClass implements Foo {
public void doSomething() {
/* All of my code goes here */
}
public void doSomethingElse() {
// I leave this unimplemented
}
}
现在我还没有doSomethingElse()
实现,将其留给子类自由实现。那是可选的。
class SubClass extends MyClass {
@Override
public void doSomethingElse() {
// Here's my implementation.
}
}
但是,如果您正在谈论Collection接口,正如其他人所说,它们是一个例外。如果某些方法未实现而您调用了这些方法,则它们可能会引发 UnsupportedOperationException
异常。
Collection接口中的可选方法意味着允许该方法的实现引发异常,但是无论如何都必须实现它。如文档中所指定:
一些集合实现对它们可能包含的元素有限制。例如,某些实现禁止使用null元素,而某些实现对其元素类型进行限制。尝试添加不合格元素会引发未经检查的异常,通常为NullPointerException或ClassCastException。尝试查询不合格元素的存在可能会引发异常,或者可能仅返回false;否则,可能会抛出异常。一些实现将表现出前一种行为,而某些将表现出后者。更一般地,尝试对不合格元素进行操作,该操作的完成不会导致将不合格元素插入集合中,这可能会导致异常或成功实现,具体取决于实现方式。此类异常标记为“可选”
new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }
;
实际上,我受到SurfaceView.Callback2的启发。我认为这是官方的方式
public class Foo {
public interface Callback {
public void requiredMethod1();
public void requiredMethod2();
}
public interface CallbackExtended extends Callback {
public void optionalMethod1();
public void optionalMethod2();
}
private Callback mCallback;
}
如果您的类不需要实现可选方法,则只需“实现回调”。如果您的类需要实现可选方法,则只需“实现CallbackExtended”。
对不起,英语不好。
在Java 8和更高版本中,此问题的答案仍然有效,但现在更加细微了。
首先,接受的答案中的这些陈述仍然正确:
那么,Java 8中新增的细微差别是什么?当谈到“可选方法”时,以下任何一种都适用:
1.一种方法,其实现在合同上是可选的
“第三条陈述”说,抽象接口方法必须始终实现,在Java 8+中仍然如此。但是,就像在Java Collections Framework中一样,可以在合同中将某些抽象接口方法描述为“可选”。
在这种情况下,实现该接口的作者可以选择不实现该方法。但是,编译器将坚持执行,因此作者将以下代码用于特定实现类中不需要的任何可选方法:
public SomeReturnType optionalInterfaceMethodA(...) {
throw new UnsupportedOperationException();
}
在Java 7和更早的版本中,这实际上是唯一的“可选方法”,即,如果未实现,则抛出UnsupportedOperationException的方法。该行为必须由接口协定(例如,Java Collections Framework的可选接口方法)指定。
2.默认方法,其重新实现是可选的
Java 8引入了默认方法的概念。这些方法的实现可以由接口定义本身提供,也可以由接口定义本身提供。通常只有在可以使用其他接口方法(即“基元”)编写方法主体,以及何时this
表示“该对象的类已实现此接口的对象” 时,才提供默认方法。
默认方法必须履行接口的约定(就像其他任何接口方法实现一样)。因此,作者可以自行决定在实现类中指定接口方法的实现(只要行为适合他或她的目的)。
在这个新环境中,Java Collections Framework 可以重写为:
public interface List<E> {
:
:
default public boolean add(E element) {
throw new UnsupportedOperationException();
}
:
:
}
这样,add()
如果实现类没有提供自己的新行为,则“可选”方法具有抛出UnsupportedOperationException的默认行为,这正是您希望发生的事情,并且符合List的约定。如果作者正在编写一个不允许将新元素添加到List实现的类,则该实现add()
是可选的,因为默认行为正是所需要的。
在这种情况下,上面的“第三条语句”仍然适用,因为该方法已在接口本身中实现。
3.返回Optional
结果的方法
最后一种新的可选方法就是返回一个的方法Optional
。本Optional
类提供处理的一个决定性的更面向对象的方式null
的结果。
以一种流畅的编程风格(例如使用新的Java Streams API进行编码时常见的那种编程风格),在任何时候为null的结果都会导致程序崩溃,并出现NullPointerException。本Optional
类提供了一种方式,使流畅的风格,而不会导致客户端代码崩溃返回null结果客户端代码的机制。
如果我们遍历grepCode中的AbstractCollection.java代码,该代码是所有集合实现的祖先类,它将帮助我们理解可选方法的含义。这是AbstractCollection类中add(e)方法的代码。add(e)方法根据收集接口是可选的
public boolean add(E e) {
throw new UnsupportedOperationException();
}
可选方法意味着它已经在祖先类中实现,并且在调用时引发UnsupportedOperationException。如果我们想使我们的集合可修改,那么我们应该重写集合接口中的可选方法。
嗯,这个话题已经解决了……是的,但是,想想,一个答案不见了。我在谈论接口的“默认方法”。例如,假设您将有一个用于关闭任何内容的类(例如析构函数之类)。假设它应该有3种方法。我们称它们为“ doFirst()”,“ doLast()”和“ onClose()”。
因此,我们说我们希望该类型的任何对象至少实现“ onClose()”,但另一个是可选的。
您可以使用接口的“默认方法”来实现这一点。我知道,这在大多数时候都可以消除接口的原因,但是如果您正在设计框架,这可能会很有用。
因此,如果您想以这种方式实现它,它将看起来如下
public interface Closer {
default void doFirst() {
System.out.print("first ... ");
}
void onClose();
default void doLast() {
System.out.println("and finally!");
}
}
现在将发生什么,例如,如果您在名为“ Test”的类中实现了该编译器,则可以使用以下代码:
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
}
输出:
first ... closing ... and finally!
要么
public class TestCloser implements Closer {
@Override
public void onClose() {
System.out.print("closing ... ");
}
@Override
public void doLast() {
System.out.println("done!");
}
}
输出:
first ... closing ... done!
所有组合都是可能的。任何带有“默认”的东西都可以实现,但是不能实现,但是任何没有的东西都必须实现。
希望我现在回答不是完全错误。
祝大家有美好的一天!
[edit1]:请注意:这仅在Java 8中有效。
我一直在寻找一种实现回调接口的方法,因此实现可选方法是必要的,因为我不想为每个回调实现每种方法。
因此,我没有使用接口,而是使用了具有空实现的类,例如:
public class MyCallBack{
public void didResponseCameBack(String response){}
}
您可以这样设置成员变量CallBack,
c.setCallBack(new MyCallBack() {
public void didResponseCameBack(String response) {
//your implementation here
}
});
然后这样称呼它。
if(mMyCallBack != null) {
mMyCallBack.didResponseCameBack(response);
}
这样,您不必担心每次回调都实现所有方法,而只需覆盖所需的方法即可。
尽管它不能回答OP的问题,但值得注意的是,从Java 8开始,向接口添加默认方法实际上是可行的。default
放置在接口的方法签名中的关键字将导致一个类可以选择重写该方法,但不要求这样做。
为了使核心集合接口的数量易于管理,Java平台没有为每种集合类型的每个变体提供单独的接口。(此类变体可能包括不可变的,固定大小的和仅附加的。)相反,每个接口中的修改操作均指定为可选-给定的实现可能选择不支持所有操作。如果调用了不受支持的操作,则集合将引发UnsupportedOperationException。实现负责记录支持哪些可选操作。所有Java平台的通用实现都支持所有可选操作。