1

Firstly, I believe my question is badly worded but don't really understand how to phrase it.

I have a starting interface that is being implemented by a number of classes. What I want to do is to see if there is a way to create a new object such that I am being passed the generic interface, then based on the method .getClass().getSimpleName(), create a new object based on that string.

Is the only way to create a switch case statement? As the number of implementing classes are too many (about 100 or so).

Reference code:

public interface MyInterface {
  public void someMethod();
}

then I would have my implementing classes:

public class MyClass1 implements MyInterface {
  public void someMethod() { //statements }
}

public class MyClass2 implements MyInterface {
  public void someMethod() { //statements }
}

public class MyClass3 implements MyInterface {
  public void someMethod() { //statements }
}

What I want to have in the end is another class which is passed an argument of type MyInterface, get the simple name from that and create a new instance of MyClassX based on that simple name.

public class AnotherClass {
  public void someMethod(MyInterface interface) {
    if (interface == null) {
      System.err.println("Invalid reference!");
      System.exit(-1);
    } else {
      String interfaceName = interface.getClass().getSimpleName();
      /**
       * This is where my problem is!
       */
      MyInterface newInterface = new <interfaceName> // where interfaceName would be MyClass1 or 2 or 3...
    }
  }
}

Any help is highly appreciated!

Mario
  • 339
  • 4
  • 17

2 Answers2

1

You can use reflection for this:

public void someMethod(MyInterface myInterface) {
Class<MyInterface> cl = myInterface.getClass();
MyInteface realImplementationObject = cl.newInstance(); // handle exceptions in try/catch block
}
Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • A little problem I have run into now, is that newInstance() assumes the given class that implements MyInterface has a constructor with no arguments. In my case, it happens to be that some of the implementing classes don't have such constructors and not all of them take the same type or even number of parameters. Some take 2 ints for example and some just a String. Is there any way around this? I have tried searching for newInstance(), getConstructor() but, as I said, this doesn't really help as not all constructors have the same data type or number of arguments being passed. – Mario Dec 01 '19 at 21:56
  • Its not a "little" problem actually. Usually things like this point on wrong design. Even if you know in "someMethod" how many parameters parameters each emplementation should have and thier types, how do you know the actual parameters values? How do know that implementation MyClass1 needs string "a" and MyClass2 needs integer "b" without knowing that these actual classes must be instantiated. – Mark Bramnik Dec 02 '19 at 04:27
  • Exactly, that's where my problem lies but I did not create the original design and can not completely change it as time does not allow to do so. I am trying to figure out the best possible solution, even the switch statement leaves me wondering how to instantiate the constructors since there will be no user interaction. I still appreciate the suggestions! – Mario Dec 02 '19 at 23:31
1

This is a common problem with many solutions. When I face it, I never use reflection because it is difficult to maintain if it is part of a big project.

Typically this problem comes when you have to build an object based on a user selection. You can try a Decorator pattern for that. So, instead of building a different object for each option. You can build a single object adding functionality depending on a selection. For instance:

// you have
Pizza defaultPizza = new BoringPizza();

// user add some ingredients
Pizza commonPizza = new WithCheese(defaultPizza);

// more interesting pizza
Pizza myFavorite = new WithMushroom(commonPizza);

// and so on ...

// then, when the user checks the ingredients, he will see what he ordered:
pizza.ingredients();
// this should show cheese, mushroom, etc.

under the hood:

class WithMushroom implements Pizza {
    private final Pizza decorated;
    public WithMushroom(Pizza decorated) {
        this.decorated = decorated;
    }
    @Override
    public Lizt<String> ingredients() {
        List<String> pizzaIngredients = this.decorated.ingredients();
        // add the new ingredient
        pizzaIngredients.add("Mushroom");
        // return the ingredients with the new one
        return pizzaIngredients;
    }
}

The point is that you are not creating an object for each option. Instead, you create a single object with the required functionality. And each decorator encapsulates a single functionality.

elbraulio
  • 994
  • 6
  • 15
  • So what you mean is that I would first create the 'vanilla' object and then based on user preference I could hard code a call to the required implementing class constructor? That still sounds like I would need a case statement though as the user has no influence on what I am trying to achieve. It is simply that I have all these classes implementing one interface which I use in a certain way throughout my project. I need a centralised method that recognizes which instance of which class has called it and create a new object based on that calling class. – Mario Dec 01 '19 at 12:50