From 63aefa41e432a050496e9f96c6ce570dde276e18 Mon Sep 17 00:00:00 2001 From: Reinier Zwitserloot Date: Mon, 26 Jan 2015 08:46:41 +0100 Subject: documentation for @Singular. --- buildScripts/website.ant.xml | 7 +- doc/changelog.markdown | 6 +- src/core/lombok/ConfigurationKeys.java | 8 +- .../lombok/eclipse/handlers/HandleBuilder.java | 2 - .../singulars/EclipseJavaUtilSingularizer.java | 2 +- src/core/lombok/javac/handlers/HandleBuilder.java | 2 - .../singulars/JavacJavaUtilSingularizer.java | 2 +- usage_examples/BuilderExample_post.jpage | 62 ++++++++ usage_examples/BuilderExample_pre.jpage | 10 ++ usage_examples/Singular-snippetExample_post.jpage | 160 +++++++++++++++++++ usage_examples/Singular-snippetExample_pre.jpage | 14 ++ .../experimental/BuilderExample_post.jpage | 40 ----- .../experimental/BuilderExample_pre.jpage | 7 - website/features/Builder.html | 172 +++++++++++++++++++++ website/features/Singular-snippet.html | 43 ++++++ website/features/SneakyThrows.html | 2 +- website/features/Value.html | 3 +- website/features/experimental/Accessors.html | 2 +- website/features/experimental/Builder.html | 113 +------------- website/features/experimental/index.html | 4 +- website/features/features.css | 2 +- website/features/index.html | 2 + 22 files changed, 489 insertions(+), 176 deletions(-) create mode 100644 usage_examples/BuilderExample_post.jpage create mode 100644 usage_examples/BuilderExample_pre.jpage create mode 100644 usage_examples/Singular-snippetExample_post.jpage create mode 100644 usage_examples/Singular-snippetExample_pre.jpage delete mode 100644 usage_examples/experimental/BuilderExample_post.jpage delete mode 100644 usage_examples/experimental/BuilderExample_pre.jpage create mode 100644 website/features/Builder.html create mode 100644 website/features/Singular-snippet.html diff --git a/buildScripts/website.ant.xml b/buildScripts/website.ant.xml index 6f54c0fd..b7a86cf3 100644 --- a/buildScripts/website.ant.xml +++ b/buildScripts/website.ant.xml @@ -149,10 +149,13 @@ such as converting the changelog into HTML, and creating javadoc. - + + + + - + diff --git a/doc/changelog.markdown b/doc/changelog.markdown index 1f0425aa..d50089af 100644 --- a/doc/changelog.markdown +++ b/doc/changelog.markdown @@ -1,10 +1,12 @@ Lombok Changelog ---------------- -### v1.14.9.shadow "Edgy Shadowy Guinea Pig" +### v1.14.9 "Edgy, Shadowy Guinea Pig" * BUGFIX: `@ExtensionMethod` was broken in Eclipse using java 8. [Issue #742](https://code.google.com/p/projectlombok/issues/detail?id=742), [Issue #747](https://code.google.com/p/projectlombok/issues/detail?id=747) -* Added a launcher to the lombok boot process which removes the need for `-Xbootclasspath` to be in your `eclipse.ini` file, and removes all non-public API and third party dependencies (such as ASM) from the lombok jar, thus removing them from your IDE's auto complete offerings in any project that uses lombok. For those debugging lombok, the launcher enables hot code replace which makes debugging a lot easier, as previously one was required to shut down the IDE, rebuild the jar, and relaunch. Add `-Dshadow.override.lombok=/path/to/lombok/bin` to the launch target for hot code replace. * BUGFIX: delombok: Using exotic characters in your source files would overzealously backslash-u escape them. Now, all characters are printed unescaped, assuming your chosen encoding can support them. Otherwise, they are escaped. [Issue #759](https://code.google.com/p/projectlombok/issues/detail?id=759) +* PROMOTION: `@Builder` has graduated from experimental to the main package with a few changes (additional of `@Singular`, removal of the `fluent` and `chain` options). The old one still exists and has been deprecated. +* FEATURE: `@Builder` now supports adding the `@Singular` annotation to any field/parameter that represents a collection, which results in a method in the generated builder that takes in one element of that collection and adds it. Lombok takes care of generating the appropriate code to produce a compacted immutable version of the appropriate type. In this version, java.util collections and guava's ImmutableCollections are supported. See the [feature documentation](http://projectlombok.org/features/BuilderSingular.html) for more information. +* FEATURE: Added a launcher to the lombok boot process which removes the need for `-Xbootclasspath` to be in your `eclipse.ini` file, and removes all non-public API and third party dependencies (such as ASM) from the lombok jar, thus removing them from your IDE's auto complete offerings in any project that uses lombok. For those debugging lombok, the launcher enables hot code replace which makes debugging a lot easier, as previously one was required to shut down the IDE, rebuild the jar, and relaunch. Add `-Dshadow.override.lombok=/path/to/lombok/bin` to the launch target for hot code replace. * Builder __TODO TODO TODO TODO DO NOT SHIP YET__: diff --git a/src/core/lombok/ConfigurationKeys.java b/src/core/lombok/ConfigurationKeys.java index 9b7c6c0a..8d8fb03b 100644 --- a/src/core/lombok/ConfigurationKeys.java +++ b/src/core/lombok/ConfigurationKeys.java @@ -176,15 +176,15 @@ public class ConfigurationKeys { */ public static final ConfigurationKey BUILDER_FLAG_USAGE = new ConfigurationKey("lombok.builder.flagUsage", "Emit a warning or error if @Builder is used.") {}; + // ----- Singular ----- + /** - * lombok configuration: {@code lombok.builder.useGuava} = {@code true} | {@code false}. + * lombok configuration: {@code lombok.singular.useGuava} = {@code true} | {@code false}. * * If explicitly set to {@code true}, guava's {@code ImmutableList} etc are used to implement the immutable collection datatypes generated by @Singular @Builder for fields/parameters of type {@code java.util.List} and such. * By default, unmodifiable-wrapped versions of {@code java.util} types are used. */ - public static final ConfigurationKey BUILDER_USE_GUAVA = new ConfigurationKey("lombok.builder.useGuava", "Generate backing immutable implementations for @Singular on java.util.* types by using guava's ImmutableList, etc. Normally java.util's mutable types are used and wrapped to make them immutable.") {}; - - // ----- Singular ----- + public static final ConfigurationKey SINGULAR_USE_GUAVA = new ConfigurationKey("lombok.singular.useGuava", "Generate backing immutable implementations for @Singular on java.util.* types by using guava's ImmutableList, etc. Normally java.util's mutable types are used and wrapped to make them immutable.") {}; /** * lombok configuration: {@code lombok.singular.auto} = {@code true} | {@code false}. diff --git a/src/core/lombok/eclipse/handlers/HandleBuilder.java b/src/core/lombok/eclipse/handlers/HandleBuilder.java index 498808bc..45f4342e 100644 --- a/src/core/lombok/eclipse/handlers/HandleBuilder.java +++ b/src/core/lombok/eclipse/handlers/HandleBuilder.java @@ -105,8 +105,6 @@ public class HandleBuilder extends EclipseAnnotationHandler { } @Override public void handle(AnnotationValues annotation, Annotation ast, EclipseNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); - long p = (long) ast.sourceStart << 32 | ast.sourceEnd; Builder builderInstance = annotation.getInstance(); diff --git a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java index e4c399ed..6661f4af 100644 --- a/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java +++ b/src/core/lombok/eclipse/handlers/singulars/EclipseJavaUtilSingularizer.java @@ -87,7 +87,7 @@ abstract class EclipseJavaUtilSingularizer extends EclipseSingularizer { protected final EclipseSingularizer guavaMapSingularizer = new EclipseGuavaMapSingularizer(); protected boolean useGuavaInstead(EclipseNode node) { - return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.BUILDER_USE_GUAVA)); + return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } protected List createJavaUtilSetMapInitialCapacitySwitchStatements(SingularData data, EclipseNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType) { diff --git a/src/core/lombok/javac/handlers/HandleBuilder.java b/src/core/lombok/javac/handlers/HandleBuilder.java index e251e0ce..4f7f79d9 100644 --- a/src/core/lombok/javac/handlers/HandleBuilder.java +++ b/src/core/lombok/javac/handlers/HandleBuilder.java @@ -86,8 +86,6 @@ public class HandleBuilder extends JavacAnnotationHandler { } @Override public void handle(AnnotationValues annotation, JCAnnotation ast, JavacNode annotationNode) { - handleExperimentalFlagUsage(annotationNode, ConfigurationKeys.BUILDER_FLAG_USAGE, "@Builder"); - Builder builderInstance = annotation.getInstance(); // These exist just to support the 'old' lombok.experimental.Builder, which had these properties. lombok.Builder no longer has them. diff --git a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java index 6aeadfea..c75dfbdd 100644 --- a/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java +++ b/src/core/lombok/javac/handlers/singulars/JavacJavaUtilSingularizer.java @@ -43,7 +43,7 @@ abstract class JavacJavaUtilSingularizer extends JavacSingularizer { protected final JavacSingularizer guavaMapSingularizer = new JavacGuavaMapSingularizer(); protected boolean useGuavaInstead(JavacNode node) { - return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.BUILDER_USE_GUAVA)); + return Boolean.TRUE.equals(node.getAst().readConfiguration(ConfigurationKeys.SINGULAR_USE_GUAVA)); } protected List createJavaUtilSetMapInitialCapacitySwitchStatements(JavacTreeMaker maker, SingularData data, JavacNode builderType, boolean mapMode, String emptyCollectionMethod, String singletonCollectionMethod, String targetType, JCTree source) { diff --git a/usage_examples/BuilderExample_post.jpage b/usage_examples/BuilderExample_post.jpage new file mode 100644 index 00000000..0446a0dc --- /dev/null +++ b/usage_examples/BuilderExample_post.jpage @@ -0,0 +1,62 @@ +import java.util.Set; + +public class BuilderExample { + private String name; + private int age; + private Set occupations; + + BuilderExample(String name, int age, Set occupations) { + this.name = name; + this.age = age; + this.occupations = occupations; + } + + public static BuilderExampleBuilder builder() { + return new BuilderExampleBuilder(); + } + + public static class BuilderExampleBuilder { + private String name; + private int age; + private java.util.ArrayList occupations; + + BuilderExampleBuilder() { + } + + public BuilderExampleBuilder name(String name) { + this.name = name; + return this; + } + + public BuilderExampleBuilder age(int age) { + this.age = age; + return this; + } + + public BuilderExampleBuilder occupation(String occupation) { + if (this.occupations == null) { + this.occupations = new java.util.ArrayList(); + } + this.occupations.add(occupation); + } + + public BuilderExampleBuilder occupations(Collection occupations) { + if (this.occupations == null) { + this.occupations = new java.util.ArrayList(); + } + this.occupations.addAll(occupations); + } + + public BuilderExample build() { + // complicated switch statement to produce a compact properly sized immutable set omitted. + // go to http://projectlombok.org/features/Singular-snippet.html to see it. + Set occupations = ...; + return new BuilderExample(name, age, occupations); + } + + @java.lang.Override + public String toString() { + return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ", occupations = " + this.occupations + ")"; + } + } +} \ No newline at end of file diff --git a/usage_examples/BuilderExample_pre.jpage b/usage_examples/BuilderExample_pre.jpage new file mode 100644 index 00000000..374799ad --- /dev/null +++ b/usage_examples/BuilderExample_pre.jpage @@ -0,0 +1,10 @@ +import lombok.Builder; +import lombok.Singular; +import java.util.Set; + +@Builder +public class BuilderExample { + private String name; + private int age; + @Singular Set occupations; +} diff --git a/usage_examples/Singular-snippetExample_post.jpage b/usage_examples/Singular-snippetExample_post.jpage new file mode 100644 index 00000000..689d97e8 --- /dev/null +++ b/usage_examples/Singular-snippetExample_post.jpage @@ -0,0 +1,160 @@ +import java.util.Collection; +import java.util.Set; +import java.util.SortedMap; +import com.google.common.collect.ImmutableList; + +public class SingularExample { + private Set occupations; + private ImmutableList axes; + private SortedMap elves; + private Collection minutiae; + + SingularExample(final Set occupations, final ImmutableList axes, final SortedMap elves, final Collection minutiae) { + this.occupations = occupations; + this.axes = axes; + this.elves = elves; + this.minutiae = minutiae; + } + + public static class SingularExampleBuilder { + private java.util.ArrayList occupations; + private com.google.common.collect.ImmutableList.Builder axes; + private java.util.ArrayList elves$key; + private java.util.ArrayList elves$value; + private java.util.ArrayList minutiae; + + SingularExampleBuilder() { + } + + public SingularExampleBuilder occupation(final String occupation) { + if (this.occupations == null) { + this.occupations = new java.util.ArrayList(); + } + + this.occupations.add(occupation); + return this; + } + + @java.lang.SuppressWarnings("all") + public SingularExampleBuilder occupations(final java.util.Collection occupations) { + if (this.occupations == null) { + this.occupations = new java.util.ArrayList(); + } + + this.occupations.addAll(occupations); + return this; + } + + public SingularExampleBuilder axis(final String axis) { + if (this.axes == null) { + this.axes = com.google.common.collect.ImmutableList.builder(); + } + + this.axes.add(axis); + return this; + } + + public SingularExampleBuilder axes(final java.lang.Iterable axes) { + if (this.axes == null) { + this.axes = com.google.common.collect.ImmutableList.builder(); + } + + this.axes.addAll(axes); + return this; + } + + public SingularExampleBuilder elf(final Integer elfKey, final T elfValue) { + if (this.elves$key == null) { + this.elves$key = new java.util.ArrayList(); + this.elves$value = new java.util.ArrayList(); + } + + this.elves$key.add(elfKey); + this.elves$value.add(elfValue); + return this; + } + + public SingularExampleBuilder elves(final java.util.Map elves) { + if (this.elves$key == null) { + this.elves$key = new java.util.ArrayList(); + this.elves$value = new java.util.ArrayList(); + } + + for (final java.util.Map.Entry $lombokEntry : elves.entrySet()) { + this.elves$key.add($lombokEntry.getKey()); + this.elves$value.add($lombokEntry.getValue()); + } + return this; + } + + public SingularExampleBuilder minutia(final java.lang.Object minutia) { + if (this.minutiae == null) { + this.minutiae = new java.util.ArrayList(); + } + + this.minutiae.add(minutia); + return this; + } + + public SingularExampleBuilder minutiae(final java.util.Collection minutiae) { + if (this.minutiae == null) { + this.minutiae = new java.util.ArrayList(); + } + + this.minutiae.addAll(minutiae); + return this; + } + + public SingularExample build() { + java.util.Set occupations; + switch (this.occupations == null ? 0 : this.occupations.size()) { + case 0: + occupations = java.util.Collections.emptySet(); + break; + + case 1: + occupations = java.util.Collections.singleton(this.occupations.get(0)); + break; + + default: + occupations = new java.util.LinkedHashSet(this.occupations.size() < 1073741824 ? 1 + this.occupations.size() + (this.occupations.size() - 3) / 3 : java.lang.Integer.MAX_VALUE); + occupations.addAll(this.occupations); + occupations = java.util.Collections.unmodifiableSet(occupations); + + } + + com.google.common.collect.ImmutableList axes = this.axes == null ? com.google.common.collect.ImmutableList.of() : this.axes.build(); + + java.util.SortedMap elves = new java.util.TreeMap(); + if (this.elves$key != null) for (int $i = 0; $i < (this.elves$key == null ? 0 : this.elves$key.size()); $i++) elves.put(this.elves$key.get($i), this.elves$value.get($i)); + elves = java.util.Collections.unmodifiableSortedMap(elves); + + java.util.Collection minutiae; + switch (this.minutiae == null ? 0 : this.minutiae.size()) { + case 0: + minutiae = java.util.Collections.emptyList(); + break; + + case 1: + minutiae = java.util.Collections.singletonList(this.minutiae.get(0)); + break; + + default: + minutiae = java.util.Collections.unmodifiableList(new java.util.ArrayList(this.minutiae)); + + } + + return new SingularExample(occupations, axes, elves, minutiae); + } + + @java.lang.Override + public java.lang.String toString() { + return "SingularExample.SingularExampleBuilder(occupations=" + this.occupations + ", axes=" + this.axes + ", elves$key=" + this.elves$key + ", elves$value=" + this.elves$value + ", minutiae=" + this.minutiae + ")"; + } + } + + @java.lang.SuppressWarnings("all") + public static SingularExampleBuilder builder() { + return new SingularExampleBuilder(); + } +} diff --git a/usage_examples/Singular-snippetExample_pre.jpage b/usage_examples/Singular-snippetExample_pre.jpage new file mode 100644 index 00000000..65f6bbc8 --- /dev/null +++ b/usage_examples/Singular-snippetExample_pre.jpage @@ -0,0 +1,14 @@ +import lombok.Builder; +import lombok.Singular; +import java.util.Collection; +import java.util.Set; +import java.util.SortedMap; +import com.google.common.collect.ImmutableList; + +@Builder +public class SingularExample { + private @Singular Set occupations; + private @Singular("axis") ImmutableList axes; + private @Singular SortedMap elves; + private @Singular Collection minutiae; +} diff --git a/usage_examples/experimental/BuilderExample_post.jpage b/usage_examples/experimental/BuilderExample_post.jpage deleted file mode 100644 index 624b236b..00000000 --- a/usage_examples/experimental/BuilderExample_post.jpage +++ /dev/null @@ -1,40 +0,0 @@ -public class BuilderExample { - private String name; - private int age; - - BuilderExample(String name, int age) { - this.name = name; - this.age = age; - } - - public static BuilderExampleBuilder builder() { - return new BuilderExampleBuilder(); - } - - public static class BuilderExampleBuilder { - private String name; - private int age; - - BuilderExampleBuilder() { - } - - public BuilderExampleBuilder name(String name) { - this.name = name; - return this; - } - - public BuilderExampleBuilder age(int age) { - this.age = age; - return this; - } - - public BuilderExample build() { - return new BuilderExample(name, age); - } - - @java.lang.Override - public String toString() { - return "BuilderExample.BuilderExampleBuilder(name = " + this.name + ", age = " + this.age + ")"; - } - } -} \ No newline at end of file diff --git a/usage_examples/experimental/BuilderExample_pre.jpage b/usage_examples/experimental/BuilderExample_pre.jpage deleted file mode 100644 index 9c754352..00000000 --- a/usage_examples/experimental/BuilderExample_pre.jpage +++ /dev/null @@ -1,7 +0,0 @@ -import lombok.experimental.Builder; - -@Builder -public class BuilderExample { - private String name; - private int age; -} diff --git a/website/features/Builder.html b/website/features/Builder.html new file mode 100644 index 00000000..e99f8525 --- /dev/null +++ b/website/features/Builder.html @@ -0,0 +1,172 @@ + + + + + + + + @Builder +
+
+
+ +

@Builder

+ +
+

Since

+

+ @Builder was introduced as experimental feature in lombok v0.12.0. +

+ @Builder gained @Singular support and was promoted to the main lombok package since lombok v1.16.0. +

+
+
+

Overview

+

+ The @Builder annotation produces complex builder APIs for your classes. +

+ @Builder lets you automatically produce the code required to have your class be instantiable with code such as:
+ Person.builder().name("Adam Savage").city("San Francisco").job("Mythbusters").job("Unchained Reaction").build(); +

+ @Builder can be placed on a class, or on a constructor, or on a static method. While the "on a class" and "on a constructor" + mode are the most common use-case, @Builder is most easily explained with the "static method" use-case. +

+ A static method annotated with @Builder (from now on called the target) causes the following 7 things to be generated:

    +
  • An inner static class named FooBuilder, with the same type arguments as the static method (called the builder).
  • +
  • In the builder: One private non-static non-final field for each parameter of the target.
  • +
  • In the builder: A package private no-args empty constructor.
  • +
  • In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. + It returns the builder itself, so that the setter calls can be chained, as in the above example.
  • +
  • In the builder: A build() method which calls the static method, passing in each field. It returns the same type that the + target returns.
  • +
  • In the builder: A sensible toString() implementation.
  • +
  • In the class containing the target: A builder() method, which creates a new instance of the builder.
  • +
+ Each listed generated element will be silently skipped if that element already exists (disregarding parameter counts and looking only at names). This + includes the builder itself: If that class already exists, lombok will simply start injecting fields and methods inside this already existing + class, unless of course the fields / methods to be injected already exist. You may not put any other method (or constructor) generating lombok annotation + on a builder class though; for example, you can not put @EqualsAndHashCode on the builder class. +

+ @Builder can generate so-called 'singular' methods for collection parameters/fields. These take 1 element instead of an entire list, and add the + element to the list. For example: Person.builder().job("Mythbusters").job("Unchained Reaction").build(); would result in the List<String> jobs + field to have 2 strings in it. To get this behaviour, the field/parameter needs to be annotated with @Singular. The feature has its own documentation. +

+ Now that the "static method" mode is clear, putting a @Builder annotation on a constructor functions similarly; effectively, + constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their + type parameters are the same as the type parameters of the class itself. +

+ Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the + @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself. If you do have an + explicit constructor, put the @Builder annotation on the constructor instead of on the class. +

+ The name of the builder class is FoobarBuilder, where Foobar is the simplified, title-cased form of the return type of the + target - that is, the name of your type for @Builder on constructors and types, and the name of the return type for @Builder + on static methods. For example, if @Builder is applied to a class named com.yoyodyne.FancyList<T>, then the builder name will be + FancyListBuilder<T>. If @Builder is applied to a static method that returns void, the builder will be named + VoidBuilder. +

+ The configurable aspects of builder are:

    +
  • The builder's class name (default: return type + 'Builder')
  • +
  • The build() method's name (default: "build")
  • +
  • The builder() method's name (default: "builder")
  • +
+ Example usage where all options are changed from their defaults:
+ @Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld")
+

+
+
+

@Singular

+

+ By annotating one of the parameters (if annotating a static method or constructor with @Builder) or fields (if annotating a class with @Builder) with the + @Singular annotation, lombok will treat that builder node as a collection, and it generates 2 'adder' methods instead of a 'setter' method. One which adds a single element to the collection, and one + which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. These 'singular' builders + are very complicated in order to guarantee the following properties: +

    +
  • When invoking build(), the produced collection will be immutable.
  • +
  • Repeatedly invoking build() works fine and does not corrupt any of the collections already generated.
  • +
  • Calling one of the 'adder' methods after invoking build() does not modify any already generated objects, and, if build() is later called again, +
  • The produced collection will be compacted to the smallest feasible format while remaining efficient.
  • +
+

+ @Singular can only be applied to collection types for which lombok has a recipe to produce the singular methods. Currently, the supported types are: +

    +
  • java.util:
      +
    • Collection and List (backed by a compacted unmodifiable ArrayList in the general case).
    • +
    • Set, SortedSet, and NavigableSet (backed by a smartly sized unmodifiable HashSet or TreeSet in the general case).
    • +
    • Map, SortedMap, and NavigableMap (backed by a smartly sized unmodifiable HashMap or TreeMap in the general case).
    • +
  • +
  • Guava's com.google.common.collect:
      +
    • ImmutableCollection and ImmutableList (backed by the builder feature of ImmutableList).
    • +
    • ImmutableSet and ImmutableSortedSet (backed by the builder feature of those types).
    • +
    • ImmutableMap, ImmutableBiMap, and ImmutableSortedMap (backed by the builder feature of those types).
    • +
  • +
+

+ If your identifiers are written in common english, lombok assumes that any collection with @Singular on it is an english plural and will attempt to automatically + singularize it. If this is possible, the add-one method will use this name. For example, if your collection is called statuses, then the add-one method will automatically + be called status. If lombok cannot singularize your identifier, or it is ambiguous, lombok will generate an error and force you to explicitly specify the singular name. + To do this, just pass the singular name as string, like so: @Singular("axis") List<Line> axes;. +

+ When using the java.util interfaces, lombok always uses ArrayList to store items added to the builder, because this is more efficient than adding them to a map or + set immediately, as lombok needs to compact and potentially duplicate the result. +

+ The snippet below does not show what lombok generates for a @Singular field/parameter because it is rather complicated. + You can view a snippet here. +

+
+
+
+

With Lombok

+
@HTML_PRE@
+
+
+
+

Vanilla Java

+
@HTML_POST@
+
+
+
+
+

Supported configuration keys:

+
+
lombok.builder.flagUsage = [warning | error] (default: not set)
+
Lombok will flag any usage of @Builder as a warning or error if configured.
+
lombok.singular.useGuava = [true | false] (default: false)
+
If true, lombok will use guava's ImmutableX builders and types to implement java.util collection interfaces, instead of creating + implementations based on Collections.unmodifiableX. You must ensure that guava is actually available on the classpath and buildpath if you use this setting. + Guava is used automatically if your field/parameter has one of the guava ImmutableX types. +
lombok.singular.auto = [true | false] (default: true)
+
If true (which is the default), lombok automatically tries to singularize your identifier name by assuming that it is a common english plural. + If false, you must always explicitly specify the singular name, and lombok will generate an error if you don't (useful if you write your code in a language other than english). +
+
+
+

Small print

+

+ @Singular support for java.util.NavigableMap/Set only works if you are compiling with JDK1.8 or higher. +

+ You cannot manually provide some or all parts of a @Singular node; the code lombok generates is too complex for this. If you want to + manually control (part of) the builder nodes associated with some field or parameter, don't use @Singular and add everything you need manually. +

+ The sorted collections (java.util: SortedSet, NavigableSet, SortedMap, NavigableMap and guava: ImmutableSortedSet, ImmutableSortedMap) require that the type argument of the collection has natural order (implements java.util.Comparable). There is no way to pass an explicit Comparator to use in the builder. +

+
+
+ +
+
+
+ + + diff --git a/website/features/Singular-snippet.html b/website/features/Singular-snippet.html new file mode 100644 index 00000000..23fcd764 --- /dev/null +++ b/website/features/Singular-snippet.html @@ -0,0 +1,43 @@ + + + + + + + + @Builder's @Singular (snippet) +
+
+
+ +

@Singular snippet

+
+
+

With Lombok

+
@HTML_PRE@
+
+
+
+

Vanilla Java

+
@HTML_POST@
+
+
+
+ +
+
+
+ + + diff --git a/website/features/SneakyThrows.html b/website/features/SneakyThrows.html index e1cd6685..1d8d3f3d 100644 --- a/website/features/SneakyThrows.html +++ b/website/features/SneakyThrows.html @@ -77,7 +77,7 @@
diff --git a/website/features/Value.html b/website/features/Value.html index 0e221b63..2e46ccf7 100644 --- a/website/features/Value.html +++ b/website/features/Value.html @@ -20,6 +20,7 @@ @Value no longer implies @Wither since lombok v0.11.8.

@Value promoted to the main lombok package since lombok v0.12.0. +

Overview

@@ -67,7 +68,7 @@
diff --git a/website/features/experimental/Accessors.html b/website/features/experimental/Accessors.html index 20590a07..65939734 100644 --- a/website/features/experimental/Accessors.html +++ b/website/features/experimental/Accessors.html @@ -97,7 +97,7 @@
diff --git a/website/features/experimental/Builder.html b/website/features/experimental/Builder.html index a8e21b9a..b335b1bc 100644 --- a/website/features/experimental/Builder.html +++ b/website/features/experimental/Builder.html @@ -12,117 +12,12 @@

@Builder

-
-

Since

-

- @Builder was introduced as experimental feature in lombok v0.12.0. -

-
-
-

Experimental

-

- Experimental because: -

    -
  • New feature - community feedback requested.
  • -
- Current status: sure thing - This feature will move to the core package soon. -
-
-

Overview

-

- The @Builder annotation produces complex builder APIs for your classes. -

- @Builder lets you automatically produce the code required to have your class be instantiable with code such as:
- Person.builder().name("Adam Savage").city("San Francisco").worksAt("Mythbusters").build(); -

- @Builder can be placed on a class, or on a constructor, or on a static method. While the "on a class" and "on a constructor" - mode are the most common use-case, @Builder is most easily explained with the "static method" use-case. -

- A static method annotated with @Builder (from now on called the target) causes the following 7 things to be generated:

    -
  • An inner static class named FooBuilder, with the same type arguments as the static method (called the builder).
  • -
  • In the builder: One private non-static non-final field for each parameter of the target.
  • -
  • In the builder: A package private no-args empty constructor.
  • -
  • In the builder: A 'setter'-like method for each parameter of the target: It has the same type as that parameter and the same name. - It returns the builder itself, so that the setter calls can be chained, as in the above example.
  • -
  • In the builder: A build() method which calls the static method, passing in each field. It returns the same type that the - target returns.
  • -
  • In the builder: A sensible toString() implementation.
  • -
  • In the class containing the target: A builder() method, which creates a new instance of the builder.
  • -
- Each listed generated element will be silently skipped if that element already exists (disregarding parameter counts and looking only at names). This - includes the builder itself: If that class already exists, lombok will simply start injecting fields and methods inside this already existing - class, unless of course the fields / methods to be injected already exist. You may not put any other method (or constructor) generating lombok annotation - on a builder class though; for example, you can not put @EqualsAndHashCode on the builder class. -

- Now that the "static method" mode is clear, putting a @Builder annotation on a constructor functions similarly; effectively, - constructors are just static methods that have a special syntax to invoke them: Their 'return type' is the class they construct, and their - type parameters are the same as the type parameters of the class itself. -

- Finally, applying @Builder to a class is as if you added @AllArgsConstructor(access = AccessLevel.PACKAGE) to the class and applied the - @Builder annotation to this all-args-constructor. This only works if you haven't written any explicit constructors yourself. If you do have an - explicit constructor, put the @Builder annotation on the constructor instead of on the class. -

- The name of the builder class is FoobarBuilder, where Foobar is the simplified, title-cased form of the return type of the - target - that is, the name of your type for @Builder on constructors and types, and the name of the return type for @Builder - on static methods. For example, if @Builder is applied to a class named com.yoyodyne.FancyList<T>, then the builder name will be - FancyListBuilder<T>. If @Builder is applied to a static method that returns void, the builder will be named - VoidBuilder. -

- The configurable aspects of builder are:

    -
  • The builder's class name (default: return type + 'Builder')
  • -
  • The build() method's name (default: "build")
  • -
  • The builder() method's name (default: "builder")
  • -
  • The 'fluent' nature of the generated 'setter'-like methods. A fluent 'setter' is named just fieldName(), a non-fluent one is named setFieldName(). (default: true)
  • -
  • The 'chain' nature of the generated 'setter'-like methods. A chainable 'setter' returns the builder object itself, letting you chain 'set' calls. A non-chainable setter returns void. (default: true)
  • -
- Example usage where all options are changed from their defaults:
- @Builder(builderClassName = "HelloWorldBuilder", buildMethodName = "execute", builderMethodName = "helloWorld", fluent = false, chain = false)
-

-
-
-
-

With Lombok

-
@HTML_PRE@
-
-
-
-

Vanilla Java

-
@HTML_POST@
-
-
-
-
-

Supported configuration keys:

-
-
lombok.builder.flagUsage = [warning | error] (default: not set)
-
Lombok will flag any usage of @Builder as a warning or error if configured.
-
-
-
-

Small print

-

- Another strategy for fluent APIs is that the programmer using your library statically imports your 'builder' method. In this case, you might want to name your builder - method equal to your type's name. So, the builder method for a class called Person would become person(). This is nicer if the builder method - is statically imported. -

- If the return type of your target static method is a type parameter (such as T), lombok will enforce an explicit builder class name. -

- You don't HAVE to use @Builder to build anything; you can for example put it on a static method that has lots of parameter to improve the API of it. - In this case, we suggest you use buildMethodName = to rename the build method to execute() instead. -

- The builder class will NOT get an auto-generated implementation of hashCode or equals methods! These would suggest that it is sensible to use - instances of a builder as keys in a set or map. However, that's not a sensible thing to do. Hence, no hashCode or equals. -

- Generics are sorted out for you. -

- If an explicit constructor is present, but @Builder is placed on the class, then the builder will be generated as if an explicit constructor is present with the - same arguments list as what @AllArgsConstructor would produce. If this constructor does not exist, a compile time error will result. Usually you should just either let - lombok make this constructor (delete your constructor from the source), or, move the @Builder annotation to the constructor. -

-
+
+ @Builder has been promoted to the core package in lombok release v1.16.0.
+ The documentation has been moved here: @lombok.Builder.
diff --git a/website/features/experimental/index.html b/website/features/experimental/index.html index f37712e0..0b9419bc 100644 --- a/website/features/experimental/index.html +++ b/website/features/experimental/index.html @@ -22,8 +22,6 @@ Features that receive positive community feedback and which seem to produce clean, flexible code will eventually become accepted as a core feature and move out of the experimental package.
-
@Builder
-
... and Bob's your uncle: No-hassle fancy-pants APIs for object creation!
@Accessors
A more fluent API for getters and setters.
@ExtensionMethod
@@ -50,6 +48,8 @@
@Value: Promoted
@Value has proven its value and has been moved to the main package. +
@Builder: Promoted
+
@Builder is a solid base to build APIs on, and has been moved to the main package.