projectlombok / lombok

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

Wither erases inherited fields #1266

Open hunter-t opened 7 years ago

hunter-t commented 7 years ago

After calling generated @Wither-method of child class all inherited fields will be erased.

Example:

public abstract class Bar {
    @Getter private long id;
    public Bar(long id) {
        this.id = id;
    }
}

@AllArgsConstructor
public class BarBell extends Bar {

    @Wither private String text;
    @Wither private int a;

    public BarBell(long id) {
        super(id);
    }
}

Now when we use generated methods from outside, variable id will be erased.

    long id = new BarBell(123L).withText("text").withA(2).getId();

    System.out.println(id);

Output: 0.

Maaartinus commented 7 years ago

Your example doesn't compile: Implicit super constructor Bar() is undefined. Must explicitly invoke another constructor.

But if it did, you'd be right. Lombok knows nothing about inherited fields and there's currently no solution for this. The problem happens already with the @AllArgsConstructor.

There's not even a good workaround. I just avoid inheriting from classes containing fields. Sometimes, it's a pain, but usually still much better than doing everything manually.

hunter-t commented 7 years ago

Sorry for not checking code, I was sure it's OK, but @AllArgsConstructor requires empty constructor in parent class, which I forgot to add.

This issue is very discouraging to me because I still have to create a ton of withSomething boilerplate, and avoiding the extension of model classes makes impossible to implement desired app design... Much hope it will be resolved...

Maaartinus commented 7 years ago

Sorry for not checking code, I was sure it's OK, but @AllArgsConstructor requires empty constructor in parent class, which I forgot to add.

That's another expression of the impossibility to access the parent class. If Lombok could access it, then it could find the non-empty constructor and call it. Note that for final fields (i.e., those @Wither is meant for) there can be no useful empty constructor.

Much hope it will be resolved...

I don't know the problem exactly (I'm just someone having contributed some small piece), but IIUIC information about other classes is not available at the time Lombok does its job. The problem is called "resolution" and blocks also other useful features.

hunter-t commented 7 years ago

A positive post here:

I continued to dive into Lombok features and found the solution of my particular problem (impossibility to chain withSomething-methods with saving field values which are already initialized).

There's no need to use @Wither, just @Setterwith @Accessors(chain = true). This will make setSomething-method return this just like withSomething-methods do.

Example:

@Accessors(chain = true)
public class StatusAdapter extends EndlessItemAdapter<Item> {

    @Setter private TCallback<Long> onUserTap;
    @Setter private TCallback<String> onImageTap;

    // ...
}

Then:

StatusAdapter adapter = new StatusAdapter()
        .setOnUserTap(id -> UserActivity.start(this, id)
        .setOnImageTap(url -> ImageActivity.start(this, url);

Hope this will help to someone.

jose-jurado commented 6 years ago

Hey thank you @hunter-t . Your tip saved my day.

Maaartinus commented 6 years ago

@jose-jurado Note that @Accessors(chain = true) does not but provide the chaining syntax possibility. The @Setter still modifies an existing object, while the @Wither returns a new object without changing the old one. Sometimes, it doesn't matter, sometimes, you need the former, sometimes, the latter.

jose-jurado commented 6 years ago

Hey thanks for the warning @Maaartinus . I was aware but it fits my scenario anyway.