Zhiroslav
Zhiroslav

Reputation: 51

Reflection API to choose between instantiation of a base class or its extended version from a different project

Let's say I have a class named Human in ProjectA. It is instantiated in the CreatureBuilder class of the same project.

Now I want to create a new class called Cyborg in a different project, i.e. in ProjectB. ProjectB has ProjectA in its imports, but ProjectA knows nothing about ProjectB.

Cyborg extends Human, and must also be instantiated by CreatureBuilder of ProjectA (so, Cyborg is located in ProjectB, I call CreatureBuilder from ProjectB to instantiate Cyborg, but CreatureBuilder is located in ProjectA, as well as my Human class).

I need a logic to create a Human when CreatureBuilder is instantiated from ProjectA, and to create a Cyborg when CreatureBuilder is instantiated from ProjectB.

I know that it can be done by creating an interface with a getCreature() method (that will be overridden in ProjectB) and a factory class. But can I use Reflection API instead? From its description, looks like it was designed to do something related (please correct me if I'm wrong). I know that reflections are slow though, and I normally avoid them. But it would be great to see a proof-of-concept, just out of curiosity.

Upvotes: 0

Views: 53

Answers (2)

Arnaud Denoyelle
Arnaud Denoyelle

Reputation: 31225

You can do that with Class.forName() if you predefine the fully qualified name of a HumanFactory.

Let's say that this HumanFactory must have this fully qualified name : com.mycompany.HumanFactory.

  • Project A can try to find such class in its classpath.
  • In case it is found, instantiate it and use it to instantiate a Human.
  • In case it is not found, instantiate a default Human class.

In Project A :

First, declare the interface of a HumanFactory :

public interface HumanFactoryItf() {
   Human newHuman();
}

Then, here is a stub for a CreatureBuilder :

public class CreatureBuilder {

  public Human build() {
    try {
      HumanFactoryItf factory = 
        (HumanFactoryItf)Class.forName("com.mycompany.HumanFactory").newInstance();
      return factory.newHuman();
    } catch(ClassNotFoundException e) {
      return new Human();
    }
  }

}

In ProjectB :

package com.mycompany;

public class HumanFactory implements HumanFactoryItf {

  public Human newHuman() {
    return new Cyborg();
  }

}

And from the main class :

public static void main(String[] args) {
  CreatureBuidler cb = ...

  /*
   This line : class.forName("com.mycompany.HumanFactory") 
   will end up instantiating the HumanFactory of ProjectB.
   */
  Human h = cb.build();
}

FYI, slf4j uses this pattern in order to decide which logger implementation to use.

Upvotes: 0

lance-java
lance-java

Reputation: 27984

Sounds like you want to use generics

public class CreatureBuilder<T extends Creature> {
    private Class<T> type;

    public CreatureBuilder<T>(Class<T> type) {
        this.type = type;
    }

    public T build() {
        return type.newInstance();
    }
}

CreatureBuilder<Human> humanBuilder = new CreatureBuilder<>(Human.class);            

Upvotes: 0

Related Questions