为啥在 Java 8 中使用 @FunctionalInterface 注解

Posted

技术标签:

【中文标题】为啥在 Java 8 中使用 @FunctionalInterface 注解【英文标题】:Why to use @FunctionalInterface annotation in Java 8为什么在 Java 8 中使用 @FunctionalInterface 注解 【发布时间】:2018-06-16 22:00:39 【问题描述】:

如果我们的接口中只有一个抽象方法,则默认为函数式接口。谁能解释一下@FunctionalInterface注解带来了什么额外的好处?

我知道如果我们添加@FunctionalAnnotation,它将不允许有人在接口中添加另一个抽象方法,因为它会给出编译错误,但我的观点是即使你不使用@FucntionalInterface注解,那么另外,如果有人添加另一个抽象方法,它会破坏代码中所有现有的 lambda 表达式,编译器会报错。

例如:

如果我有以下接口:

public interface User 

    Integer fetchData(Integer userId);

具有以下实现:

public class UserImpl implements User

    @Override
    public Integer fetchData(Integer userId) 
        return 1;
    

及以下用法:

公共类 TestFunctionalInterface

public static void main(String[] args) 
    User user = a -> a*2;
    System.out.println("FetchedData:"+user.fetchData(2));

现在,如果我尝试在界面中添加另一个方法,如下所示:

public interface User 

    Integer fetchData(Integer userId);

    Integer fetchLoginDetails(Integer userId);


编译器在下面的代码中抱怨:

public class TestFunctionalInterface 

    public static void main(String[] args) 
        User user = a -> a*2;
        System.out.println("FetchedData:"+user.fetchData(2));
    


在行用户用户 = a -> a*2;

带有消息“此表达式的目标类型必须是函数 界面”。

【问题讨论】:

除非您的编译单元中没有该类型的任何 lambda。它也可用作文档。并且某些 SAM 接口旨在定义为 lambda 表达式。见***.com/questions/28166187/… @JB Nizet,感谢您的回复。如果我的编译单元中没有任何 lambda,即使在这种情况下,如果我在接口中添加另一个抽象方法,我应该很确保所有要实现这个接口的类都会失败,因为它们需要实现这个新方法。 除非您在编译单元中没有此接口的任何具体实现。您可能会编写一个库,定义一个接口,但不自己实现它。同样,它还用于记录该接口设计为实现为 lambda 表达式。 What is use of Functional Interface in Java 8?的可能重复 【参考方案1】:

功能接口只能有一个抽象方法。如果你有两个抽象方法,那么你的接口就不再起作用了。

如果您有一种抽象方法,则可以使用 lambda 表达式。

如果您看到 @FunctionalInterface 注释,您就知道不应添加任何新方法,因为它会破坏设计。

如果你向任何 java 接口添加新的抽象方法,它无论如何都会破坏代码,因为你需要为具体类提供实现

【讨论】:

【参考方案2】:

如果不同的模块正在使用该接口,则不会发生编译错误,例如,如果该接口通过依赖项可用。使用您的模块的人可以安全地在 lambda 中使用该函数,而不必担心以后的更新会破坏他们的代码。

【讨论】:

【参考方案3】:

@functionalInterface 保护接口的主要优点是当你使用 lambda 实例化它们时。

Lambda 声明只能声明一个代码块,所以如果你的接口没有保护,有些人会添加一个抽象方法,你的 lambda 就没有更多意义了......

这就是为什么强烈建议不要使用 lambda 实现一些隐式函数接口。

因此,如果您想通过 lambda 方式实现此接口,我鼓励您添加作为安全性的注释。如果你不想要这种实现,或者你的界面会改变或者有改变的风险,那就不要了

【讨论】:

【参考方案4】:

限制开发者

是的,这个@FunctionalInterface 注释可以确保开发人员在这个接口中只写一个抽象方法,为什么?因为如果还有一个抽象类意味着 lambda 表达式 就失去了它的功能。

正确的例子

@FunctionalInterface
public interface TestFun 
     void test();

不正确的代码

@FunctionalInterface
public interface TestFun 
     void test();
     void m(); // new methods added will lost @FunctionalInterface it's feature 

什么想法?

让我们使用TestFun 接口使用lambda 表达式。

public class App 

    public static void main(String[] args) 
        TestFun t = () -> System.out.println("I'm working"); ;
    

正如我们看到使用t = ()-> 而不告诉我们的代码我们想直接使用void test(),如果我们编写两个方法,就像我们在第二个中编写的那样,我们的想法就来了,所以我们不能使用 lambda 表达式 在这种情况下,我们应该使用@FunctionalInterface 来限制开发人员只能使用一种抽象方法。

下面的代码是来自Runnable接口和Thread类的真实示例。

Thread 类:

public class Thread implements Runnable 
  *// .......* 

Runnable接口:

@FunctionalInterface
public interface Runnable 
    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();

【讨论】:

以上是关于为啥在 Java 8 中使用 @FunctionalInterface 注解的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Java 8 lambda 从流中获取一系列项目?

为啥不应该在参数中使用 Java 8 的 Optional

为啥不应该在参数中使用 Java 8 的 Optional

Java系列教程一起爪哇Java 8——函数式接口

为啥 Java 8 接口方法中不允许使用“final”?

为啥 Java 8 的 Cloneable 中没有默认的 clone()