projectlombok / lombok

Very spicy additions to the Java programming language.
https://projectlombok.org/
Other
12.83k stars 2.37k forks source link

[Feature Request] Make Lombok generated constructor ignore fields #1085

Open mattsains opened 8 years ago

mattsains commented 8 years ago

It would be great if there was an annotation which allowed you to tell Lombok to ignore a field entirely. This would benefit people looking for things like @AllArgsConstructor(except = {"field"})

Example:

@Value @Builder
class Regex {
    private final String regex;
    @LombokIgnore
    private Pattern compiledRegex;

    public Pattern getCompiledRegex() {
        if (this.compiledRegex == null) {
            this.compiledRegex = Pattern.compile(regex);
        }
        return this.compiledRegex;
    }
}

For cases where you want to do anything out of the ordinary with a field, you're left fighting with Lombok over control of the field. For instance, the same behaviour in Lombok as it is now would be pretty hard to accomplish, as far as I'm aware.

For instance, you might want to:

jasperdj commented 6 years ago

Is this still being looked into? Is this a technically viable feature request? The absence of any issue management is quite daunting in the Lombok team.

Maaartinus commented 6 years ago

Is this still being looked into?

I guess, it's got just forgotten. There are so many new feature requests opened monthly, that implementing them could keep a big company busy all the time. What's worse: They don't fit together.

Is this a technically viable feature request?

Most probably, yes. But is completely ignoring the field the best possibility? What if a final field without an initializer gets ignored?

getCompiledRegex looks like a lazy getter. Then all you need is @EqualsAndHashCode(exclude={"compiledRegex"}) and a manual constructor annotated with @Builder (the setter gets suppressed automatically).

So maybe @AllArgsConstructor(exclude = {"compiledRegex"}) and @Builder automatically using this constructor would be better?

The absence of any issue management is quite daunting in the Lombok team.

Yes (I'm not a member).

aburach commented 6 years ago

+1 for to have possibility exclude particular field(s) from builder with a simple annotation, or e.g. by @Builder(exclude={"",""}). Because currently for to exclude a field from being generated Builder, I had to manually define Constructor (without lombok annotations) and put @Builder annotation above it. Such implementation looked awkward.

joshfix commented 6 years ago

I just stumbled upon the @RequiredArgsConstructor. It will build a constructor for only final fields and fields annotated with @NonNull. So it's inclusive rather than exclusive, but the same effect can be achieved.

burdoto commented 4 years ago

Consider this Issue: #2267

Now see this fixing change I have made to my project related to that Issue:

    private @Getter @Setter OkHttpClient httpClient;

    public final CompletableFuture<Void> lazyLoading;

    { // Initialization
        // Initialize httpClient first so that lazyLoading will always have the client ready.
        httpClient = new OkHttpClient.Builder().build();
        lazyLoading = CompletableFuture.allOf(requestLanguages(), requestCategories());
    }

I am using the instance initializer to order the initialization of httpClient and lazyLoading, but this opened a new problem: Since the lazyLoading field now has no value attached directly to it, the Builder now tries to generate a setter function for that field; which will fail, since the lazyLoading field is final. grafik This could be circumvented by an ignoring-annotation, like here:

    public @Builder.Ignore final CompletableFuture<Void> lazyLoading;

Now, lombok no longer would try to generate a Builder setter to that value and my problem would finally be solved.

EDIT: Before this suggestion gets made, because of code styling preferences I will NOT implement the whole AllArgsConstructor myself for the sake of solving this problem. In that case, Lombok would permanently be removed from my "useful dependencies" toolkit, since the whole reason of using Lombok is to not be required to have code clutter (like an AllArgsConstructor would be).

Maaartinus commented 4 years ago

@burdoto I'm afraid, you're doing something wrong. IMHO the core problem is that you're assigning a running CompletableFuture which needs a different field to field. I'm not sure how it should work in the builder, where the different field doesn't exist yet.

Anyway, I'd suggest to construct the CodeBottle without started threads and add a method for starting them (this should be simple to do with CompletableFuture). Then you can use your original code with @Builder.Default and you may avoid other problems, too.

IIUIC @Builder.Ignore is not the solution. IIRC @Builder doesn't use setters, it uses a constructor. Builder must ignore assigned fields and no annotation should be needed.... I guess, your use of initializer confused it into believing that lazyLoading is not initialized.

This should be equivalent code without the problem:

private @Getter @Setter OkHttpClient httpClient = 
    new OkHttpClient.Builder().build();
public final CompletableFuture<Void> lazyLoading = 
    CompletableFuture.allOf(requestLanguages(), requestCategories());
burdoto commented 4 years ago

[@Maaartinus]

Sorry, but I don't understand the meaning of your first part.

When constructing the CodeBottle instance, I NEED lazy loading to start already due to future changes that will require it to run right on construction.

When doing it the way you suggested, I lose power over execution order, which was a problem mentioned earlier in this issue. Before, I had my solution just the way you provided, which didn't work.

Maaartinus commented 4 years ago

When constructing the CodeBottle instance, I NEED lazy loading to start already due to future changes that will require it to run right on construction.

Can't you simply replace all occurrences of new CodeBottle(....) by new CodeBottle(....).start() or provide a static factory method returning already started instances (this is no problem, only the construction is tricky)?

Before, I had my solution just the way you provided, which didn't work.

AFAIK no. Before, you used @Builder.Default, which broke it (by delaying the initialization of httpClient). Without it, it should be fine.

burdoto commented 4 years ago

In any case, this is not an issue to discuss my way of solving my project, this issue is about requesting a feature arguing with a project. Since there is a template for feature requests, I assume those are welcome.

Maaartinus commented 4 years ago

@burdoto

Disclaimer: I'm not a Lombok team member. Just trying to help.

Lombok is not a library.... it's a tool which can do things others can't do and it's way more difficult to implement and maintain any feature in Lombok than in a library (I hacked on Lombok some time ago and I do some code generation, too; so I'm perfectly sure about the workload ratio being something like 1:100).

Any new feature has it's (huge) cost and may or may not help users. Therefore, I'm trying to show the alternatives, which may be better than the feature requested.

Feature requests are surely welcome, it's just that there are too many of them and too few contributors.

silkentrance commented 4 years ago

@burdoto @mattsains have you tried annotating your to be ignored fields with

@Getter(AccessLevel.NONE)
@Setter(AccessLevel.NONE)

?

Of course, a shortcut such as @Ignore would be great.

Why does the builder still provide a setter for something with AccessLevel.NONE?

Maaartinus commented 4 years ago

@silkentrance

Why does the builder still provide a setter for something with AccessLevel.NONE?

With @Getter(AccessLevel.NONE) @Setter(AccessLevel.NONE) you said, there should be no getter and no setter in the class, but the field still exists and will be set in the constructor, so the builder has a field for it, too. And a field in the builder which can't be set makes no sense.

silkentrance commented 4 years ago

@Maaartinus see my Why does the builder... comment.

Rawi01 commented 4 years ago

You can add $ as prefix to the field name, lombok ignores all these fields

silkentrance commented 4 years ago

@Rawi01 yeah, this works, although... nevermind, will go for that approach for now.

silkentrance commented 4 years ago

@Rawi01 I have found a solution to this, without having to prefix the field with $

@Data
@Builder
public class Data {
  private final List<String> someStringList = new ArrayList<>();
}

for the above, no builder method will be generated unless you annotate the above field with @Builder.Default.

carlosromanno commented 1 year ago

Is this proposal still unanswered?

DoctoRJurius commented 1 month ago

still answered...