Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add @EnumHelpers #240

Open
4 of 5 tasks
JarvisCraft opened this issue Aug 30, 2021 · 1 comment
Open
4 of 5 tasks

Add @EnumHelpers #240

JarvisCraft opened this issue Aug 30, 2021 · 1 comment
Assignees
Labels
enhancement New feature or request
Milestone

Comments

@JarvisCraft
Copy link
Owner

JarvisCraft commented Aug 30, 2021

Description

Implement @EnumHelpers annotation and the corresponsing annotation processor to generate boilerplate enum methods.

Problem

While Java's enum is a powerful mechanism for describing varios constructions, it has a number of negative aspects which make it not that helpful.

The biggest problem is static E @NotNull [] values() method which is required to always return a fresh copy of the array due to array mutability although it's usually needed for iteration.

Another serious design problem (especially considering padla's philosophy) is inability to do try-match operation, i.e. to try to find an enum constant by a string name alternatively fallin back to null or default value etc.

This PR addresses this by provifing a @EnumHelpers annotation causing the generation of ES utility class for enum E.

Goals

  • Add @EnumHelper allowing generation of:
    • @NotNull Stream<@NotNull E> stream()
    • @NotNull @Unmodifiable List<@NotNull E> values()
    • @Nullable E match(final @NotNull String name) and similar ones
  • implement annotation-processor to generate the helper class

Example

Given

@EnumHelpers
public enum Foo {
    A, B, C
}

the following should be generated:

@Generated
public final class Foos {

    private static final @NotNull Foo @NotNull [] VALUES_ARRAY
            = Foo.values(); // may be used for faster iterations
    private static final @NotNull List<@NotNull Foo> VALUE_LIST
            = Collections.unmodifiableList(Arrays.asList(VALUES_ARRAY));

    private Foos() {
        throw new AssertionError("Foos is an utility class and thus should never be instantiated");
    }

    public static @NotNull Stream<@NotNull Foo> stream() {
        return Arrays.stream(VALUES_ARRAY);
    }

    public static @NotNull @Unmodifiable List<@NotNull Foo> values() {
        return VALUES_LIST;
    }

    public static @Nullable Foo match(final @NotNull String name) {
        if (name == null) throw new NullPointerException("name is null");

        switch (name) {
            case "A": return A;
            case "B": return B;
            case "C": return C;
            default: return null;
        }
    }

    public static Foo match(final @NotNull String name, final Foo defaultValue) {
        if (name == null) throw new NullPointerException("name is null");

        switch (name) {
            case "A": return A;
            case "B": return B;
            case "C": return C;
            default: return defaultValue;
        }
    }

    // note: possibly worth specialized lambda
    public static @Nullable Foo match(final @NotNull String name, final @NotNull Supplier<Foo> defaultValueSupplier) {
        if (name == null) throw new NullPointerException("name is null");
        if (defaultValueSupplier== null) throw new NullPointerException("defaultValueSupplieris null");

        switch (foo) {
            case "A": return A;
            case "B": return B;
            case "C": return C;
            default: return defaultValueSupplier.get();
        }
    }

    // ...
}

Notes

Generated class should net rely on Lombok as it may be not used by annotation users.

Subjects to discuss:

  • Documenting annotation should be generated if possible(?) but it is not clear whether some eaxct one (org.jetbrains) should be used or custom (via an annotation parameter).
@JarvisCraft JarvisCraft added the enhancement New feature or request label Aug 30, 2021
@JarvisCraft JarvisCraft added this to the 1.0.0 milestone Aug 30, 2021
@JarvisCraft JarvisCraft self-assigned this Aug 30, 2021
@slutmaker
Copy link

slutmaker commented Oct 14, 2021

Suggestions:

  • use following naming: Foos.stream(), Foos.list(), Foos.array()
  • use List.of() instead of Collections.unmodifiableList(Arrays.asList()) for Java 9+
  • make Foos.match generation optional
  • add additional annotation to generate match-like methods for finding by field:
@RequiredArgsConstructor
@Getter
enum Foo {
  FOO_BAR("foo-bar"),
  BAR_BAZ("bar-baz");

  @EnumHelpers.Finder
  private final String value;
}


class Foos {

  public static Foo fromValue(String value) {
    // some optimal finding logic
    // for example: 
    //   1) use HashMap for big enums and array-foreach for small enums
    //   2) switch-case if enum field is compile-time constant
  }

}
  • add additional annotation to generate enum name constants and field constants:
@RequiredArgsConstructor
@Getter
@EnumHelpers.NameConstants
enum Foo {
  FOO_BAR("foo-bar"),
  BAR_BAZ("bar-baz");

  @EnumHelpers.FieldConstants
  private final String value;
}

class Foos {
  public static final String FOO_BAR_NAME = "FOO_BAR";
  public static final String BAR_BAZ_NAME = "BAR_BAZ";

  public static final String FOO_BAR_VALUE = "foo-bar";
  public static final String BAR_BAZ_VALUE = "bar-baz";
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants