0

I have a parent class and a child class shown below:

@Component
public class Parent {
  public AndroidDriver<MobileElement> driver;

  public void method() {};
}


@Component
public class Child extends Parent {

  @Override
  public void method() {

     //do something with the parent's driver variable
     driver.findElement(...);
  }
}

Now in my main class, I injected these two classes. For the record I have to set the driver variable like below after I got some inputs from the user.

@Resource
private Parent parent;

@Resource
private Child child;

private static ConfigurableApplicationContext applicationContext;

public static void main(String[] args)
{
     applicationContext = SpringApplication.run(DemoAppiumProjectApplication.class, args);
}

@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx)
    {
        //Get some value from the user input, then set driver;
        parent.setDriver(new AndroidDriver<>(${some input from the user}));

        child.method();
    }

My guess is that the parent.setDriver(driver) is set but it's not the parent instance of the injected child class that it extends, so when it goes down to child.method, the driver variable is null and end up with NullPointerException.

I can call child.setDriver to achieve my goal, but what if I have a set of children and I only want the setDriver to be called once by the parent, then all children class can access it. How can I do this correctly?

Lee Aron
  • 33
  • 4
  • 1
    I would suggest not letting the `Child` class extend the `Parent` class but instead plug in the instance of the `Parent` class into the child class. No need for inheritance here. – Amir Schnell Apr 20 '20 at 07:19
  • Good idea! But the catch is I would have to inject parent into every child class, maybe that's necessary? I can't quite determine which is better. Can't inheritance work in this situation? – Lee Aron Apr 20 '20 at 07:36

1 Answers1

1

What happens when you execute your code is as follows:

@Resource
private Parent parent;

This creates a bean of type Parent, which will be used everywhere you inject a Parent field. For simplicity let's call the created object: object1

parent.setDriver(new AndroidDriver<>(${some input from the user}));

This is executed on object1 obviously. object1 now has a driver set.

@Resource
private Child child;

Here, a bean of type Child is created. Since object1 is a bean of type Parent, it cannot be reused, so a new bean has to be created. This is object2.

child.method();

...
@Override
  public void method() {

     //do something with the parent's driver variable
     driver.findElement(...);
  }

What happens here is that you try to call the driver on object2, but this driver was never set. This is why the problem occurs.

Solution:

The only solution I can currently think of is to inject the Parent bean into the Child class:

@Component
public class Parent {
  private AndroidDriver<MobileElement> driver;

  public AndroidDriver<MobileElement> getDriver() {
    return driver;
  }

  public void setDriver(AndroidDriver<MobileElement> driver) {
    this.driver = driver;
  }
}


@Component
public class Child {

  private Parent parent;

  @Autowired //or @Resource, depends on what you want
  public Child(Parent parent) {
    this.parent = parent;
  }

  public void method() {

     this.parent.getDriver().findElement(...);
  }
}

Main class

@Resource
private Parent parent;

@Resource
private Child child;

private static ConfigurableApplicationContext applicationContext;

public static void main(String[] args)
{
     applicationContext = SpringApplication.run(DemoAppiumProjectApplication.class, args);
}

@Bean
public CommandLineRunner commandLineRunner(ApplicationContext ctx)
    {
        //Get some value from the user input, then set driver;
        parent.setDriver(new AndroidDriver<>(${some input from the user}));

        child.method();
    }

This also means that every prior subclass now has to be injected with the Parent bean, but this is something you will not get rid of.

I hope this helps.

Amir Schnell
  • 611
  • 4
  • 11
  • Just to be clear if the purpose is to inject the Parent Bean, why is the @Autowired annotation on the constructor method of Child instead of Parent declaration in Child class? – Lee Aron Apr 21 '20 at 02:08
  • 1
    Using `@Autowired` on the constructor is a design pattern in spring. There is a post [here](https://stackoverflow.com/a/40620318/7546121), which also has a blogpost linked to why you should use this pattern. I hope this answeres your question. – Amir Schnell Apr 21 '20 at 06:09