简体   繁体   中英

Java Chain Inheritance with Lombok and Guice Injections

EDIT: This question really should be around Lombok and Guice instead of vanilla java inheritance.

I'm trying to implement a Java inheritance chain with Lombok and Guice injections, it works something like this:

Class 1

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

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

Class 2

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

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

Class 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
    }
}

Out there in the main program I have code that calls wolf.method1() . The problem here is that only wolf has all the attributes needed (due to Guice injections), whereas all the fields in Animal are undefined. I suspect I can probably do it in Vanilla Java, but things are going to get super messy (I have 6 attributes in Animal class and 5 more in Mammal ). Is there a way to mix-and-match lombok's annotations (@NoArgsConstructor, @AllArgsConstructor, etc) to make this work?

Thanks.

Your abstract classes cannot be directly instantiated even if you add public constructors, because they are declared abstract. If you prefer, you can make the constructors protected to indicate they are only available to subclasses.

right that abstract classes can't really be instantiated, but why didn't the Java compiler catch this and stop complaining about not having a constructor?

Any class you write without an explicit constructor has an implicit no-args constructor. Any implicit no-args constructor implicitly calls its superclass's no-args constructor, even if that superclass is abstract. So if some class up the chain doesn't have a no-args constructor (because you explicitly gave it another constructor), then you code won't compile.

In the code you gave in your question, there are no explicit constructors, so every class does have an implicit no-args constructor. In your actual code, presumably you have written a constructor somewhere, which is why the implicit no-args constructor has not been added.

I propose:

  1. use only interfaces for the entire hierarchy.
  2. decouple behaviors and encapsulate them in separate classes.
  3. use composition instead of inheritance, ie private SomeBehavior someBehavior; for every specific animal that needs it.

It will make your design better and solve the problem as well.

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() {
   }
}

and then

public class Sheperd implements Dog {
    private TakeADump dumpService;

    private TakeApee peeService;
}

And now your dog can s... and p... :)

Also add

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

Of course it will make sense to create an abstract Animal.

ie

public class AbstractAnimal {
        private TakeADump dumpService;

        private TakeApee peeService;  

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

then

public abstract class AbstractDog extends AbstractAnimal implements Dog {
}

and

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);
    }
}

So your mistake is using too much abstract when you can use interfaces.

When you are implementing concept of inheritance with some argument constructor it is good practice to have one default (no argument) constructor defined there. Because while creating object of child class compiler internally will call constructor of parent class. eg

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
 }

if anyhow you have implemented argument constructor in parent class then compiler will not implement default constructor implicitly we need to define it explicit to make call to super constructor from child constructor. Or do a explicit call to argument constructor from child constructor.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM