【问题标题】:Every interface with a single abstract method should be a functional interface? [duplicate]每个具有单个抽象方法的接口都应该是功能接口? [复制]
【发布时间】:2020-01-21 18:19:34
【问题描述】:

最近我开始了一个新项目,其中所有具有单个抽象方法的接口都使用@FunctionalInterface 进行注释。我经常看到人们在接口中添加另一个抽象方法后删除了注释。我什么时候问为什么了?他们告诉我,一个不在公司的人告诉他们那样做。现在我不确定对显然不会与 lambda 一起使用的接口进行注释是否是个好主意。

现在我看到一条有用的信息是人们甚至在服务中的服务中使用注释。我只是看起来像这样的代码:

@FunctionalInterface
public interface SomeService {
    void doSomething();
}

【问题讨论】:

  • 如果添加其他方法,则必须删除注释,因为编译会报错。
  • @matt 更重要的是,如果其他人用该注释标记了接口,则不应向接口添加抽象方法。该注释的主要用途之一(至少我使用它的方式)是用于同事校对。它说“我在某个地方的 lambda 中使用这个接口,所以不要添加任何方法。”如果有人在不是这种情况的接口上添加注释,那是一回事,但你最好确保你没有破坏任何依赖于该接口的东西。
  • 文档真的很有帮助,现在我觉得自己像个白痴一样在这里问它而不先检查文档。我不认为放注解的人知道如何使用它,我认为他们看到一个带有单个抽象方法的接口并自动放注。

标签: java java-8 functional-interface


【解决方案1】:

我不确定对明显的接口进行注释是否是个好主意 不会与 lambdas 一起使用

不是。充其量,这是浪费时间。在最坏的情况下,它会产生误导。

The documentation

[用来表示类型]打算是一个功能接口

如果您说它“显然不会”在 lambda 中使用,那么使用标记您确实的注释会认为这种用法是一个明显的矛盾。

【讨论】:

    【解决方案2】:

    说明

    每个只提供一个(抽象)方法的接口,即没有实现的接口,都是所谓的功能接口

    无论是明确的还是隐含的。

    注解@FunctionalInterface@Override 一样,是可选的,如果你想创建一个功能接口。

    所以如果你声明了一个接口@FunctionalInterface,但它实际上不止一个这样的方法,编译器会帮助你并防止编译出错。告诉你你违背了自己的意图。

    如果你没有注解,它会编译,但它不再是一个功能接口。这对您来说可能是一个错误,因为您的意图是创建一个功能界面。请注意,如果您实际上是在实际需要函数式接口的环境中使用该接口,例如对于 lambda,那么一旦将另一个方法添加到接口中,它显然就不会再编译了。

    这里有一些例子:

    // is a functional interface
    interface Foo {
        void bar();
    }
    
    // is a functional interface
    @FunctionalInterface
    interface Foo {
        void bar();
    }
    
    // is not a functional interface
    interface Foo {
        void bar();
        void baz();
    }
    
    // does not compile
    @FunctionalInterface
    interface Foo {
        void bar();
        void baz();
    }
    

    使用

    这是为了让编译器、其他团队成员和你未来的自己清楚你的意图。这样,编译器可以帮助您发现错误,以防万一您搞砸了。

    如果您在团队中工作并设计界面,则可能是另一种情况。如果您没有通过注释或注释/文档明确将此接口标记为功能接口,则其他团队成员可能不知道您的意图并为其添加更多方法。

    如果您是库设计师,即编写供其他外部人员使用的代码,这一点尤其重要。如果你标记你的界面@FunctionalInterface,这是对他们的承诺,你打算保持这种状态。例如,他们可以安全地将其用于 lambda。不用担心他们的代码会在您向您的库发布更新后立即中断。

    相反的情况也是如此。如果他们发现您的接口只有一个方法,但没有明确注释,他们会明白这并不意味着用作功能接口,即使它目前是一个。因此,他们不会将它用于 lambda,尽管他们可以,因为您可能会在未来的更新中更改它。


    详情

    您可以阅读JLS§9.8. Functional Interfaces中的精确定义:

    函数式接口是一个只有一个抽象方法(除了 Object 的方法)的接口,因此代表一个单一的函数契约。这种“单一”方法可以采用多个抽象方法的形式,这些方法具有从超接口继承的重写等效签名;在这种情况下,继承的方法在逻辑上代表一个方法。

    还有JLS§9.6.4.9. @FunctionalInterface

    注解类型FunctionalInterface 用于指示接口是功能接口(第9.8 节)。它有助于及早发现出现在本应具有功能的接口中或由其继承的不适当方法声明。

    如果接口声明使用@FunctionalInterface 注释但实际上不是函数式接口,则会出现编译时错误。

    由于某些接口是函数式的,因此没有必要或不希望所有函数式接口的声明都使用@FunctionalInterface 进行注释。

    这里的最后一段尤其重要。 IE。您可能偶然创建了一个功能接口,并计划稍后添加更多功能。所以人们不应该把它与 lambdas 一起使用,否则当你添加更多方法时,他们的代码会中断。


    注意

    如前所述,@Override 标记的工作方式相同,但用于覆盖方法。因此,您也可以在没有它的情况下覆盖方法。但是如果你使用它并且可能打错了,即你实际上并没有覆盖某些东西,编译器会帮助你立即发现这个问题。

    【讨论】:

    • 如果函数式接口在您的代码中用作 lambda,无论如何您都会收到编译器错误。主要优点是库代码可以记录作为(并且可能仍然是)功能接口的接口。在业务代码中,注解没有什么价值。
    • 不,请仔细阅读完整答案。尤其是“详情”的最后一节。它是一个只有在您打算创建功能界面时才应使用的工具。如果您偶然创建了一个(但计划稍后更改),则不会。
    • @RichardMarto 不可以,但是可以使用注解表示不应该添加抽象方法。如果其他人添加了注释,那么您不应该添加抽象方法,除非您有信心不会破坏任何东西。请记住,最初的问题不是关于 OP 是否应该添加注释,而是关于当其他人错误地应用它时该怎么办。在尝试纠正错误之前,您需要非常确定它实际上是错误的。
    • 我明白了,谢谢。
    • (非默认)方法,即没有实现”是一种冗长的说法,abstract 而且它是模棱两可的,因为静态方法有一个实现,但不是“默认”的。那么为什么不使用既定的简单术语“抽象”呢?除此之外,向接口添加更多抽象方法会破坏所有实现,无论它们是被实现为 lambda 表达式还是普通类。
    猜你喜欢
    • 1970-01-01
    • 2016-04-30
    • 1970-01-01
    • 2017-09-22
    • 2020-05-18
    • 1970-01-01
    • 2012-09-12
    • 2012-07-19
    • 1970-01-01
    相关资源
    最近更新 更多