Mario
Mario

Reputation: 817

Creating objects in java, without knowing the Class at compile time

I have a Java question. I have a configuration file that I read and extract some paths to java classes, let's say to an ArrayList of strings (for convenience I will call it path2libs).

I want to create an instance for every one of these classes dynamically by iterating this 'path2libs' array. I tried out something like this:

Class clazz = Class.forName("test.Demo"); 
Demo demo = (Demo) clazz.newInstance();

But I can't know what classes I will read, so I cannot do the appropriate casting.

When this is done, I want to call a method on each object (it is the same method in each case). All of these objects are based on an interface so I know their methods.

So, basically (to summarize) I have two questions :)

  1. Create the desirable objects
  2. Call a method on each object

Upvotes: 3

Views: 4660

Answers (3)

Radiodef
Radiodef

Reputation: 37845

"Class cannot be converted to MyInterface"

This sounds like a compiler error you'd get after trying to cast a Class in to a MyInterface like this:

MyInterface mi = (MyInterface)Class.forName("test.Demo");

This is not valid, a MyInterface is not a Class so the compiler can determine the cast can never be legal. Rather, you should cast the new instance to the MyInterface. Now, there is a way you can do this in a more type-safe way:

Class<?> wildCls = Class.forName("test.Demo");

Class<? extends MyInterface> miSubtypeCls = (
    wildCls.asSubclass(MyInterface.class)
);

MyInterface miSubtype = miSubtypeCls.newInstance();

This is similar to a non-generic version like this:

Class rawCls = Class.forName("test.Demo");

MyInterface miSubtype = (MyInterface)rawCls.newInstance();

However, the first version doesn't use raw types (which are not recommended) and instead, asSubclass will throw the ClassCastException if test.Demo does not implement MyInterface. The method asSubclass is provided for this type of scenario.

Here's a short demonstration:

package test;

public class HelloAsSubclass
implements Runnable {
    @Override
    public void run() {
        System.out.println("hello from Runnable implementation!");
    }

    public static void main(String[] args) {
        try {
            Class<?> wildCls = Class.forName("test.HelloAsSubclass");

            Class<? extends Runnable> rSubtypeCls = (
                wildCls.asSubclass(Runnable.class)
            );

            Runnable r = rSubtypeCls.newInstance();
            r.run();

        } catch(
            ClassNotFoundException |
            InstantiationException |
            IllegalAccessException ex
        ) {
            ex.printStackTrace();
        }
    }
}

Also of course make sure that your subtypes have a public no-argument constructor (or no constructor as in the above example and is essentially the same thing) which is a requirement of newInstance. Otherwise you would need to instantiate them by getting a Constructor object.

Upvotes: 2

Sotirios Delimanolis
Sotirios Delimanolis

Reputation: 279960

There are a number of issues.

First, Class#newInstance() expects the corresponding class to have a public constructor with an empty parameter list. You therefore have to make sure all the classes you want to work with have that. Otherwise, you'll need to get an appropriate Constructor, make it accessible, and provide the appropriate arguments.

Second, do you know the interface that all classes share at run time? If you do, you can cast all the instances to that interface type

InterfaceType interfaceInstance = (InterfaceType) clazz.newInstance();
interfaceInstance.interfaceMethod(/* arguments */);

If you don't, you'll have to again rely on reflection.

You'll need to retrieve the Method for the corresponding method, providing the types of each parameter in the method's parameter list.

Method method = clazz.getDeclaredMethod("methodName" /*, possible parameter list types */);

You can then invoke that method on your class' instance, again providing the appropriate arguments.

method.invoke(interfaceInstance /*, possible arguments */);

Upvotes: 2

T-Bull
T-Bull

Reputation: 2146

You're doing that quite right. Only, you wouldn't cast to Demo, but to the common interface instead. After all, you want to do something common with all those instances. You want to call methods on them. Somehow you have to know that these methods are actually available. That is what interfaces are for.

Upvotes: 0

Related Questions