如何在龙目岛调用超级构造函数

Posted

技术标签:

【中文标题】如何在龙目岛调用超级构造函数【英文标题】: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 中是否有注释可以做到这一点?

【问题讨论】:

【参考方案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 的正确注释时使用的解决方案。

以上是关于如何在龙目岛调用超级构造函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Python 中调用超级构造函数?

如何:安全地调用具有不同参数的超级构造函数

龙目岛没有创建默认构造函数导致杰克逊数据绑定失败[重复]

调用超级构造函数时尝试资源

隐式超级构造函数 Person() 未定义。必须显式调用另一个构造函数?

爪哇。隐式超级构造函数 Employee() 未定义。必须显式调用另一个构造函数[重复]