@FunctionalInterface 在分阶段构建器中的好处

Benefits of @FunctionalInterface in staged builders

简介

在网上搜索时,我偶然发现了 Benoit Tellier post Next level Java 8 staged builders 的博客,他在博客中分享了他针对某些用例的分阶段构建器模式的变体。

我注意到阶段用 @FunctionalInterface 注释。这是他 post 的一个例子(没有技巧):

public static class MailboxCreatedBuilder {
    @FunctionalInterface
    public interface RequireUser {
        RequireSessionId user(User user);
    }

    @FunctionalInterface
    public interface RequireSessionId {
        RequireMailboxId sessionId(MailboxSession.SessionId sessionId);
    }

    @FunctionalInterface
    public interface RequireMailboxId {
        FinalStage mailboxId(MailboxId mailboxId);
    }

    public static class FinalStage {
        ...
        ...
    }

    public static RequireUser builder() {
        return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId);
    }
}

此注释将一个阶段可以拥有的方法数量限制为一个,加上具有默认实现的重载方法。无论如何,每个阶段处理一个 属性 可能是个好主意,但对于我当前的需求,我希望在单独的 class.

中有多个方法和实现。

问题

不过这让我想知道:我的舞台也应该有 @FunctionalInterface 吗? benefits/how 这样的构建器会在函数式编程中使用什么?

编辑

根据这个问题下面的评论,我真正想知道的是 needs/benefits 是什么让阶段遵守功能接口契约(不管可选注释)。

单一方法所需的阶段类型用于强制调用者提供所需的值,由编译器强制执行(它也强制执行特定的顺序)。 FinalStage class 然后具有所有可选方法。

在这种情况下,在调用 builder() 之后,您 必须 调用 user(...),然后是 sessionId(...),然后是 mailboxId(...),最后是 FinalStage:

中定义的任何可选方法
MailboxCreatedBuilder.builder()
        .user(...)
        .sessionId(...)
        .mailboxId(...)
        ...
        .build();

在此模式中,您的 RequireUserRequireSessionIdRequireMailboxId 需要是功能接口,以便 MailboxCreatedBuilder 类型的 builder() 方法能够看起来像

public static RequireUser build() {
    return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId);
}

如果任何阶段不遵循功能接口契约(即它不是单一抽象方法接口),那么这个链条就会中断,您将需要一个具体的非抽象 class对于那个阶段,可能还有它之后的所有阶段。

此处的 @FunctionalInterface 注释在此模式中是可选的(因为您可以使用单个抽象方法从任何接口创建 lambda),它旨在通知您的同事和编译器这些 classes 确实需要成为整个设置工作的功能接口。由于 JLS 需要所有 Java 编译器来验证具有该注释的类型的功能接口契约,因此拥有注释对您有好处,因此当您不小心破坏契约时,您会得到很好的编译器错误。