Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

lombok @Builder(toBuilder = true) compilation error when used on constructor of a sub class

Tags:

java

lombok

My code is as followd

package test.lombok;

import lombok.*;

@AllArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
public class SuperClass {

    private int foo;

    @Getter
    public static class SubClass extends SuperClass {

        private int bar;

        @Builder(toBuilder = true)
        private SubClass(int foo, int bar) {
            super(foo);
            this.bar = bar;
        }

    }
}

As showed above, I'm trying to use @Builder(toBuilder = true) on a sub class.

When toBuilder set to false, there is no problem at all.

But when I set toBuilder = true, I got an compilation error "Error:java: foo has private access in test.lombok.SuperClass".

I'm wondering why does this happen and how to fix this.

like image 662
Juan Zhong Avatar asked Sep 29 '17 03:09

Juan Zhong


3 Answers

Lombok tries to create toBuilder method in SubClass when the attribute toBuilder is set to true in annotation @Builder. The method returns the SubClassBuilder class. Here is the what the toBuilder method would look like,

public SuperClass.SubClass.SubClassBuilder toBuilder() {
    return (new SuperClass.SubClass.SubClassBuilder())
                    .foo(this.foo).bar(this.bar);
}

As you notice, toBuilder method tries to access foo attribute directly and not by method getFoo. Since foo is private and belongs to the parent class, SuperClass, you get the following error:

Error:java: foo has private access in test.lombok.SuperClass

like image 174
Indra Basak Avatar answered Oct 24 '22 06:10

Indra Basak


The problem is because of how the toBuilder method is implemented in SubClass:

public SuperClass.SubClass.SubClassBuilder toBuilder() {
    return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
}

Instead of this.foo it should be super.foo, and the code would compile. Accessing super.foo is possible in this case because SubClass is an inner class of the SuperClass, otherwise, Java would disallow super.foo too.

If you want to see the code generated by lombok by for yourself, declare foo as public, then compile, then delombok (or decompile) and you will see code like this (then change the property to private to see where the error occurs):

import java.beans.ConstructorProperties;

public class SuperClass {
    public int foo;

    @ConstructorProperties({"foo"})
    protected SuperClass(int foo) {
        this.foo = foo;
    }

    public int getFoo() {
        return this.foo;
    }

    public static class SubClass extends SuperClass {
        private int bar;

        private SubClass(int foo, int bar) {
            super(foo);
            this.bar = bar;
        }

        public static SuperClass.SubClass.SubClassBuilder builder() {
            return new SuperClass.SubClass.SubClassBuilder();
        }

        public SuperClass.SubClass.SubClassBuilder toBuilder() {
            return (new SuperClass.SubClass.SubClassBuilder()).foo(this.foo).bar(this.bar);
        }

        public int getBar() {
            return this.bar;
        }

        public static class SubClassBuilder {
            private int foo;
            private int bar;

            SubClassBuilder() {
            }

            public SuperClass.SubClass.SubClassBuilder foo(int foo) {
                this.foo = foo;
                return this;
            }

            public SuperClass.SubClass.SubClassBuilder bar(int bar) {
                this.bar = bar;
                return this;
            }

            public SuperClass.SubClass build() {
                return new SuperClass.SubClass(this.foo, this.bar);
            }

            public String toString() {
                return "SuperClass.SubClass.SubClassBuilder(foo=" + this.foo + ", bar=" + this.bar + ")";
            }
        }
    }
}

EDIT: Thanks to @maaartinus for pointing me to super.foo, the answer is updated with that info.

like image 3
Daniel Avatar answered Oct 24 '22 06:10

Daniel


AFAICT this is a Lombok bug. There are three ways, how to access foo and only one of them works:

  • plain foo leads to "Cannot make a static reference to the non-static field foo"
  • this.foo as used by Lombok leads to "The field SuperClass.foo is not visible"
  • super.foo works!

AFAIK everything declared in the same source file is accessible somehow, but finding the proper expression might be tricky.

like image 1
maaartinus Avatar answered Oct 24 '22 06:10

maaartinus