Java接口中的可选方法

时间:2012-05-13 14:47:36

标签: java methods interface

根据我的理解,如果在java中实现接口,则该接口中指定的方法必须由实现所述接口的子类使用。

我注意到在某些接口(如Collection接口)中有一些方法被注释为可选,但这究竟是什么意思?它让我有点兴奋,因为我认为界面中指定的所有方法都是必需的?

12 个答案:

答案 0 :(得分:210)

这里的答案似乎有很多混乱。

Java语言要求接口中的每个方法都由该接口的每个实现实现。期。 此规则没有例外。要说"收藏是一个例外"建议对这里真正发生的事情有一个非常模糊的理解。

重要的是要认识到有两个级别符合界面:

  1. Java语言可以检查的内容。这几乎归结为:每个方法都有一些实现吗?

  2. 实际履行合同。也就是说,实现是否按照界面中的文档说它应该做什么?

    编写良好的界面将包含准确解释实现期望的文档。您的编译器无法为您检查。您需要阅读文档,并按照他们的意思行事。如果你不做合同所说的那样,那么就编译器而言,你将有一个接口的实现,但它将是一个有缺陷/无效的实现。< / p>

  3. 在设计Collections API时,Joshua Bloch决定不再使用非常精细的接口来区分不同的集合变体(例如:可读,可写,随机访问等),而是只有非常粗略的一组接口,主要是 CollectionListSetMap,然后将某些操作记录为&#34;可选&#34;。这是为了避免由细粒度界面引起的组合爆炸。来自Java Collections API Design FAQ

      

    为了说明血腥细节中的问题,假设您要添加   层次结构的可修改性概念。你需要四个新的   interfaces:ModifiableCollection,ModifiableSet,ModifiableList和   ModifiableMap。以前简单的层次结构现在是一个混乱的   异质观。此外,您还需要一个新的Iterator接口   不可修改的集合,不包含删除操作。   你现在可以取消UnsupportedOperationException吗?不幸   不

         

    考虑数组。它们实现了大部分List操作,但没有   删除并添加。他们是#34;固定大小&#34;列表。如果你想捕捉   在层次结构中的这个概念,您必须添加两个新接口:   VariableSizeList和VariableSizeMap。你不必添加   VariableSizeCollection和VariableSizeSet,因为它们是   与ModifiableCollection和ModifiableSet相同,但您可以   为了一致性,无论如何都要选择添加它们。此外,你需要一个新的   各种不支持添加和删除的ListIterator   操作,与不可修改的List一起使用。现在我们最多十个或者   12个接口,加上两个新的Iterator接口,而不是我们的   原四。我们完了吗?否。

         

    考虑日志(例如错误日志,审核日志和日志)   可恢复的数据对象)。它们是天然附加序列,   支持除remove和set之外的所有List操作   (更换)。它们需要一个新的核心接口和一个新的迭代器。

         

    那么不可变的集合呢,而不是不可修改的集合呢?   (即,客户不能更改的集合,永远不会   因任何其他原因而改变)。许多人认为这是最多的   所有的重要区别,因为它允许多个线程   无需同步即可同时访问集合。   将此支持添加到类型层次结构还需要四个   接口

         

    现在我们有大约20个接口和5个迭代器,而且它已经完成了   几乎可以肯定,在实践中仍然存在收藏品   不适合任何接口。例如,   Map返回的集合视图是自然删除集合。   此外,有些集合会拒绝某些元素   它们的价值基础,所以我们仍然没有完成运行时间   异常。

         

    当一切都说完了,我们觉得这是一个合理的工程   通过提供一个非常小的集来妥协以回避整个问题   可能引发运行时异常的核心接口。

    当Collections API中的方法被记录为&#34;可选操作&#34;时,并不意味着您可以将方法实现留在实现中,也不意味着您可以使用空方法身体(一方面,他们中的许多人需要返回一个结果)。相反,它意味着有效的实现选择(仍然符合合同的选择)是抛出UnsupportedOperationException

    请注意,因为UnsupportedOperationExceptionRuntimeException,所以就编译器而言,您可以从任何方法实现中抛出它。例如,您可以从Collection.size()的实现中抛出它。但是,这样的实现会违反合同,因为Collection.size()的文档并未说明这是允许的。

    除此之外:Java的Collections API使用的方法有些争议(现在可能比首次引入的时候少)。在一个完美的世界中,接口具有可选操作,而是使用细粒度接口。问题是Java既不支持推断的结构类型也不支持交集类型,这就是为什么尝试以正确的方式做事的原因&#34;在收藏的情况下,最终变得非常笨拙。

答案 1 :(得分:25)

为了编译接口的实现(非抽象)类 - 必须实现所有方法。

但是,如果我们想到一个方法,它的实现是一个简单的异常抛出'非实现'(就像Collection接口中的一些方法),那么{{在这种情况下,接口是例外,而不是常规情况。 通常是,实现类应该(并且将会)实现所有方法。

集合中的“可选”意味着实现类不必“实现”(根据上面的术语)它,它只会抛出NotSupportedException)。

不可变集合的一个很好的示例 - Collection方法 - 具体只会实现一个除了抛出add()之外什么都不做的方法

NotSupportedException的情况下,它是为了防止凌乱的继承树,这将使程序员痛苦 - 但对于大多数案例,这个范例不建议,并且应该避免,如果可能的。


<强>更新

从java 8开始,引入了default method

这意味着,接口可以定义一个方法 - 包括它的实现。
这是为了允许为接口添加功能而添加的,同时仍然支持不需要新功能的代码的后向兼容性。

请注意,该方法仍由声明它的所有类实现,但使用接口的定义。

答案 2 :(得分:16)

Collection接口中的可选方法意味着允许该方法的实现抛出异常,但无论如何都必须实现它。按照规定in the docs

  

某些集合实现对元素有限制   他们可能包含。例如,某些实现禁止null   元素,有些元素的元素类型有限制。   尝试添加不合格的元素会引发未经检查的异常,   通常是NullPointerException或ClassCastException。试图   查询不合格元素的存在可能会抛出异常,或者   它可能只是返回虚假;一些实现将展示   以前的行为和一些将展示后者。更普遍,   尝试对完成的不合格元素进行操作   不会导致不合格的元素插入   集合可以抛出异常,也可以成功,可以选择   实施。此类例外标记为“可选”   此接口的规范。

答案 3 :(得分:16)

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个例外。

答案 4 :(得分:9)

必须为编译代码实现所有方法(除了那些在Java 8+中具有default实现的方法),但实现不必做任何功能上有用的事情。具体来说,它:

  • 可能是空白的(空方法。)
  • 可能会抛出一个UnsupportedOperationException(或类似的)

后一种方法通常在集合类中使用 - 所有方法仍然实现,但有些可能会在运行时调用异常。

答案 5 :(得分:4)

如果我们在grepCode中查看AbstractCollection.java的代码,它是所有集合实现的祖先类,它将帮助我们理解可选方法的含义。这是AbstractCollection类中add(e)方法的代码。根据{{​​3}}接口

,add(e)方法是可选的
public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

可选方法意味着它已在祖先类中实现,并在调用时抛出UnsupportedOperationException。如果我们想让我们的集合可以修改,那么我们应该覆盖集合界面中的可选方法。

答案 6 :(得分:4)

事实上,我受到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”。

抱歉屎英语。

答案 7 :(得分:4)

嗯,这个话题已经被贴到了...是的......但是想想,一个答案就不见了。我正在谈论&#34;默认方法&#34;接口 例如,让我们假设您有一个用于关闭任何东西的类(如析构函数或其他东西)。让我们说它应该有3种方法。让他们称呼他们&#34; doFirst()&#34;,&#34; doLast()&#34;和&#34; onClose()&#34;。

所以我们说我们希望任何类型的对象至少实现&#34; onClose()&#34;,但另一个是可选的。

你可以意识到,使用&#34;默认方法&#34;接口我知道,这将大部分时间否定接口的原因,但如果你正在设计一个框架,这可能很有用。

因此,如果你想以这种方式实现它,它将看起来如下

public interface Closer {
    default void doFirst() {
        System.out.print("first ... ");
    }
    void onClose();
    default void doLast() {
        System.out.println("and finally!");
    }
}

现在会发生什么,如果你在一个名为&#34; Test&#34;的类中实现它,编译器将完全没有以下内容:

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!

所有组合都是可能的。任何&#34;默认&#34;可以实施,但不能,但必须实施任何事情。

希望我现在回答并非完全错误。

每个人都过得愉快!

[edit1]:请注意:这仅适用于Java 8。

答案 8 :(得分:4)

在Java 8及更高版本中,这个问题的答案仍然有效,但现在更加细致入微了。

首先,接受的答案中的这些陈述仍然是正确的:

  • 接口用于指定合同中的隐式行为(实现类必须遵守的行为规则声明才能被视为有效)
  • 合同(规则)与实施(规则的程序化编码)之间存在区别
  • 接口中指定的方法必须始终实施(在某些时候)

那么,Java 8中新增的细微差别是什么?当谈到&#34;可选方法&#34; 以下任何一个现在都适合:

<强> 1。一种方法,其实施是合同可选的

&#34;第三个陈述&#34;说必须始终实现抽象接口方法,这在Java 8+中仍然适用。但是,与Java Collections Framework一样,可以将一些抽象接口方法描述为&#34; optional&#34;在合同中。

在这种情况下,实现接口的作者可以选择不实现该方法。但是,编译器将坚持实现,因此作者将此代码用于特定实现类中不需要的任何可选方法:

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

在Java 7及更早版本中,这实际上是唯一一种&#34;可选方法&#34;有一种方法,即一种方法,如果没有实现,则抛出UnsupportedOperationException。此行为必须由接口契约指定(例如,Java Collections Framework的可选接口方法)。

<强> 2。一种默认方法,其重新实现是可选的

Java 8引入了默认方法的概念。这些方法的实现可以由接口定义本身提供。通常只有在可以使用其他接口方法(即&#34;基元&#34;)编写方法体时提供默认方法,并且this可以表示&#34;此对象的class已实现此接口。&#34;

默认方法必须满足接口的约定(就像任何其他接口方法实现一样)。因此,在实现类中指定接口方法的实现是由作者自行决定的(只要行为适合他或她的目的)。

在这个新环境中,Java Collections Framework 可以重写为:

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

通过这种方式,&#34;可选&#34;如果实现类没有提供自己的新行为,那么方法add()具有抛出UnsupportedOperationException的默认行为,这正是您希望发生的并且符合List的合同。如果作者正在编写一个不允许将新元素添加到List实现的类,则add()的实现是可选的,因为默认行为正是所需的。

在这种情况下,&#34;第三个陈述&#34;上面仍然适用,因为该方法已在接口本身实现。

第3。返回Optional结果

的方法

最后一种新的可选方法只是一种返回Optional的方法。 Optional类提供了一种更加面向对象的方法来处理null结果。

在一种流畅的编程风格中,例如在使用新的Java Streams API进行编码时常见的类型,任何时候的null结果都会导致程序因NullPointerException而崩溃。 Optional类提供了一种机制,用于以一种启用流畅样式的方式将null结果返回给客户端代码,而不会导致客户端代码崩溃。

答案 9 :(得分:0)

虽然它没有回答OP的问题,但值得注意的是,从Java 8开始向接口is in fact doable添加默认方法。放置在接口方法签名中的default关键字将导致类具有覆盖该方法的选项,但不需要它。

答案 10 :(得分:0)

我一直在寻找一种实现回调接口的方法,因此实现可选方法是必要的,因为我不想为每次回调实现每个方法。

因此,我使用了一个空实现的类,而不是使用接口,如:

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);
}

这样,您不必担心每次回调都会实现每个方法,但只能覆盖您需要的方法。

答案 11 :(得分:0)

Oracle's Java Collections Tutorial:

  

为了使核心集合接口的数量可管理,Java平台没有为每种集合类型的每个变体提供单独的接口。 (这种变体可能包括不可变的,固定大小的和仅附加的。)相反,每个接口中的修改操作都指定为可选-给定的实现可能选择不支持所有操作。如果调用了不受支持的操作,则集合将引发UnsupportedOperationException。实现负责记录它们支持哪些可选操作。所有Java平台的通用实现都支持所有可选操作。