【问题标题】:how to Call super constructor in Lombok如何在龙目岛调用超级构造函数
【发布时间】:2015-06-26 17:13:26
【问题描述】:

我有课

@Value
@NonFinal
public class A {
    int x;
    int y;
}

我还有一个 B 班

@Value
public class B extends A {
    int z;
}

lombok 抛出错误,说它找不到 A() 构造函数,显式调用它我想要 lombok 做的是给类 b 注释,以便它生成以下代码:

public class B extends A {
    int z;
    public B( int x, int y, int z) {
        super( x , y );
        this.z = z;
    }
}

我们在 Lombok 中是否有注释可以做到这一点?

【问题讨论】:

    标签: java lombok


    【解决方案1】:

    这在龙目岛是不可能的。虽然这将是一个非常好的特性,但它需要解析才能找到超类的构造函数。超类只有在 Lombok 被调用时才知道名称。使用 import 语句和类路径来查找实际的类并非易事。而且在编译过程中,你不能只使用反射来获取构造函数列表。

    这并非完全不可能,但使用val@ExtensionMethod 中的分辨率的结果告诉我们,这很难且容易出错。

    披露:我是 Lombok 开发人员。

    【讨论】:

    • @roel-spilker 我们了解其背后的复杂性。但是Lombok能否为构造函数注解提供inConstructor方法,我们可以指定Lombok在生成的构造函数中注入super的哪个构造函数?
    • afterConstructor 也可以做一些自动初始化
    • @Manu/@Pawel:查看 lombok 增强请求:github.com/peichhorn/lombok-pg/issues/78(当前开放)
    • 还是不行?
    • @Roel 是否可以制作类似@CopyConstructor 的东西?这样我们就可以用它注释class Base,并且在class Der extends Base 中,以下内容很简短,很容易手动完成:public Der(Base base, Object extraProp) { super(base); this.extraProp = extraProp; }
    【解决方案2】:

    Lombok Issue #78 引用此页面https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ 并附有这个可爱的解释:

    @AllArgsConstructor 
    public class Parent {   
         private String a; 
    }
    
    public class Child extends Parent {
      private String b;
    
      @Builder
      public Child(String a, String b){
        super(a);
        this.b = b;   
      } 
    } 
    

    因此,您可以像这样使用生成的构建器:

    Child.builder().a("testA").b("testB").build(); 
    

    official documentation 对此进行了解释,但没有明确指出您可以通过这种方式促进它。

    我还发现这与 Spring Data JPA 配合得很好。

    【讨论】:

    • 您能否提供一个与 Spring Data JPA 一起使用的示例?
    • 这根本不能回答问题。相反,它是手工完成的,而问题是如何生成它。同时,通过拖拽@Builder让整个事情变得更加混乱,这与问题没有任何关系。
    • 其实这对于那些只想创建继承结构然后使用builder的人来说非常有用。这是我使用#lombok 的 99% 的原因。有时我们只需要手工制作东西就可以让它按照我们想要的方式工作。所以感谢@jj-zabkar
    • 然后;只需编写 self+parent arg 构造函数即可。无需使用构建器
    • 它可以在 STS 和 eclipse 中工作,但是当你生成应用程序的 JAR 文件时,它很可能会失败。我尝试了 Both SuperBuilder, Builder 的继承。两个都失败了。小心!!
    【解决方案3】:

    Lombok 1.18 版引入了@SuperBuilder 注解。我们可以使用它以更简单的方式解决我们的问题。

    您可以参考https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3

    所以在您的子类中,您将需要这些注释:

    @Data
    @SuperBuilder
    @NoArgsConstructor
    @EqualsAndHashCode(callSuper = true)
    

    在你的父类中:

    @Data
    @SuperBuilder
    @NoArgsConstructor
    

    【讨论】:

    • 对于 SuperBuilder,AllArgsConstructor 是否包含来自父子节点的参数?谢谢!
    • 如果结果类中不需要 SuperBuilder,我们无法解决这个问题。尤其是当这个建造者离理想很远的时候。就我而言,我的构建者总是在做深拷贝,龙目岛 - 不。
    • java:使用对 java.lang.Object 的超级调用生成 equals/hashCode 是没有意义的。
    【解决方案4】:

    Lombok 不支持通过创建任何 @Value 注释类 final (如您所知道的使用 @NonFinal)来指示的。

    我发现的唯一解决方法是自己声明所有成员为 final 并改用 @Data 注释。这些子类需要由 @EqualsAndHashCode 注释,并且需要一个显式的 all args 构造函数,因为 Lombok 不知道如何使用超类的所有 args 之一创建一个:

    @Data
    public class A {
        private final int x;
        private final int y;
    }
    
    @Data
    @EqualsAndHashCode(callSuper = true)
    public class B extends A {
        private final int z;
    
        public B(int x, int y, int z) {
            super(x, y);
            this.z = z;
        }
    }
    

    尤其是子类的构造函数,对于有很多成员的超类来说,解决方案有点凌乱,抱歉。

    【讨论】:

    • 你能解释一下为什么“子类需要用@EqualsAndHashCode注释”吗? @Data中不包含这个注解吗?谢谢 :)
    • @GerardB @Data 也创建了 equals() 和 hashCode() 但不关心任何继承。为了确保使用超类 equals() 和 hashCode(),您需要使用 callSuper 显式生成
    【解决方案5】:

    对于有很多成员的超类,我建议你使用@Delegate

    @Data
    public class A {
        @Delegate public class AInner{
            private final int x;
            private final int y;
        }
    }
    
    @Data
    @EqualsAndHashCode(callSuper = true)
    public class B extends A {
        private final int z;
    
        public B(A.AInner a, int z) {
            super(a);
            this.z = z;
        }
    }
    

    【讨论】:

    • 这个方法很有意思,赞!
    • @Delegate@Target({ElementType.FIELD, ElementType.METHOD})AInner 应该是A中的字段。
    【解决方案6】:

    如果子类的成员比父类多,则可以做得不是很干净,但方法很短:

    @Data
    @RequiredArgsConstructor
    @EqualsAndHashCode(callSuper = true)
    @ToString(callSuper = true)
    public class User extends BaseEntity {
        private @NonNull String fullName;
        private @NonNull String email;
        ... 
    
        public User(Integer id, String fullName, String email, ....) {
            this(fullName, email, ....);
            this.id = id;
        }
    }
    
    @Data
    @AllArgsConstructor
    abstract public class BaseEntity {
       protected Integer id;
    
       public boolean isNew() {
          return id == null;
       }
    }
    

    【讨论】:

    • 不应轻易保护实例变量,因为它很可能违反了面向对象的原则。
    【解决方案7】:

    作为一个选项,您可以使用com.fasterxml.jackson.databind.ObjectMapper 从父类初始化子类

    public class A {
        int x;
        int y;
    }
    
    public class B extends A {
        int z;
    }
    
    ObjectMapper MAPPER = new ObjectMapper(); //it's configurable
    MAPPER.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
    MAPPER.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );
    
    //Then wherever you need to initialize child from parent:
    A parent = new A(x, y);
    B child = MAPPER.convertValue( parent, B.class);
    child.setZ(z);
    

    如果需要,您仍然可以在 A 和 B 上使用任何 lombok 注释。

    【讨论】:

    • 这不适用于 OP 的 @Value,因为如果没有无参数构造函数,Jackson 将失败。即使它确实有效,序列化为 json 并在每个 new 上反序列化也是疯狂的。
    • 不幸的是,这不是问题的答案(是否有任何 Lombok 注释可以解决所描述的问题?)
    • @LarsGendner 是的,我猜这不是一个直接的答案。但这是我在等待来自 lombok 的正确注释时使用的解决方案。
    猜你喜欢
    • 1970-01-01
    • 2016-07-16
    • 1970-01-01
    • 1970-01-01
    • 2012-10-27
    • 2011-01-24
    • 2021-11-23
    • 2021-06-29
    相关资源
    最近更新 更多