@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();
在此模式中,您的 RequireUser
、RequireSessionId
和 RequireMailboxId
需要是功能接口,以便 MailboxCreatedBuilder
类型的 builder()
方法能够看起来像
public static RequireUser build() {
return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId);
}
如果任何阶段不遵循功能接口契约(即它不是单一抽象方法接口),那么这个链条就会中断,您将需要一个具体的非抽象 class对于那个阶段,可能还有它之后的所有阶段。
此处的 @FunctionalInterface
注释在此模式中是可选的(因为您可以使用单个抽象方法从任何接口创建 lambda),它旨在通知您的同事和编译器这些 classes 确实需要成为整个设置工作的功能接口。由于 JLS 需要所有 Java 编译器来验证具有该注释的类型的功能接口契约,因此拥有注释对您有好处,因此当您不小心破坏契约时,您会得到很好的编译器错误。
简介
在网上搜索时,我偶然发现了 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();
在此模式中,您的 RequireUser
、RequireSessionId
和 RequireMailboxId
需要是功能接口,以便 MailboxCreatedBuilder
类型的 builder()
方法能够看起来像
public static RequireUser build() {
return user -> sessionId -> mailboxId -> new FinalStage(user, sessionId, mailboxId);
}
如果任何阶段不遵循功能接口契约(即它不是单一抽象方法接口),那么这个链条就会中断,您将需要一个具体的非抽象 class对于那个阶段,可能还有它之后的所有阶段。
此处的 @FunctionalInterface
注释在此模式中是可选的(因为您可以使用单个抽象方法从任何接口创建 lambda),它旨在通知您的同事和编译器这些 classes 确实需要成为整个设置工作的功能接口。由于 JLS 需要所有 Java 编译器来验证具有该注释的类型的功能接口契约,因此拥有注释对您有好处,因此当您不小心破坏契约时,您会得到很好的编译器错误。