Lombok和Guice注入的Java链继承

时间:2018-09-28 08:28:25

标签: java inheritance dependency-injection guice lombok

编辑:这个问题确实应该围绕Lombok和Guice展开,而不是香草java继承。

我正在尝试通过Lombok和Guice注入实现Java继承链,它的工作原理如下:

1级

public abstract class Animal { 
    @NonNull protected String attr1;
    protected abstract void method1();

    void method0() {
        // Some code that uses attr1
    }
}

第2类

public abstract class Mammal extends Animal { 
    @NonNull protected String attr2;
    protected abstract void method2();

    @Override 
    void method1() {
        // some logic that uses attr2
        method2();
    }
}

3级

public class Wolf extends Mammal { 
    @Inject @NonNull private String attr1;
    @Inject @NonNull private String attr2;
    @Inject @NonNull private String attr3;

    @Override 
    void method2() {
        // some logic
    }
}

main程序中,我有调用wolf.method1()的代码。这里的问题是只有wolf具有所需的所有属性(由于Guice注入),而Animal中的所有字段都是未定义的。我怀疑我可以在Vanilla Java中做到这一点,但是事情将变得一团糟(Animal类中有6个属性,Mammal中有5个属性)。有没有一种方法可以混合和匹配lombok的注释(@ NoArgsConstructor,@ AllArgsConstructor等)来使之工作?

谢谢。

3 个答案:

答案 0 :(得分:1)

即使添加公共构造函数,您的抽象类也无法直接实例化,因为它们被声明为抽象的。如果愿意,可以使构造函数protected表示它们仅可用于子类。

  

对抽象类不能真正实例化是正确的,但是Java编译器为什么没有抓住这个并停止抱怨没有构造函数?

您编写的没有显式构造函数的任何类都具有隐式的无参数构造函数。任何隐式的无参数构造函数都隐式调用其超类的无参数构造函数,即使该超类是抽象的。因此,如果链中某个类没有no-args构造函数(因为您显式地为其指定了另一个构造函数),则您的代码将无法编译。

在您提供的问题代码中,没有显式构造函数,因此每个确实类都有一个隐式的无参数构造函数。在您的实际代码中,大概是在某个地方写了一个构造函数,这就是为什么未添加隐式no-args构造函数的原因。

答案 1 :(得分:0)

我建议:

  1. 仅将接口用于整个层次结构。
  2. 解耦行为并将其封装在单独的类中。
  3. 使用合成代替继承,即私有SomeBehavior someBehavior;对于需要它的每种特定动物。

这将使您的设计更好,也可以解决问题。

i.e. 
public interface Mammals {
}

public interface Animal extends Mammals {
}

public interface Dog extends Animal {
}

and 
public class TakeADump {
   public void dump() {
   }
}

public class TakeAPee {
   public void pee() {
   }
}

然后

public class Sheperd implements Dog {
    private TakeADump dumpService;

    private TakeApee peeService;
}

现在你的狗可以...和......)

也添加

public class F... {
    public void f...(<Animal> animal) {
        // ... check it's an instance of the same or compatible animal or throw UnsupportedOperationException() if it's incompatible
    }
}

:D

当然,创建抽象动物是有意义的。

public class AbstractAnimal {
        private TakeADump dumpService;

        private TakeApee peeService;  

        private F... f...Service;
}

然后

public abstract class AbstractDog extends AbstractAnimal implements Dog {
}

public class Sheperd extends AbstractDog {
    public void lookAfterSheep() {
        Sheep sheep = SheepLocator.findNearest();
        // pee on a sheep
        peeService.pee(sheep);
        // dump on a sheep
        dumpService.dump(sheep);
        // f... a sheep
        f...Service.mount(sheep);
    }
}

因此,当您可以使用接口时,您的错误是使用了太多抽象。

答案 2 :(得分:0)

使用某些参数构造函数实现继承的概念时,最好在其中定义一个默认(无参数)构造函数。因为在创建子类的对象时,编译器会在内部调用父类的构造函数。 例如

class ABC {

 }
 class XYZ implements ABC{

 }
 public class Test{
  XYZ obj= new XYZ() // this will internally call default constructor of XYZ and
  //in that first statement will super()--> this will call default constructor of class ABC
 }

无论如何,如果您已经在父类中实现了参数构造函数,那么编译器将不会隐式实现默认构造函数,我们需要显式定义它,以便从子构造函数调用超级构造函数。或从子构造函数中显式调用参数构造函数。