32

I want to instantiate a class by the value of a String. I found several tutorials that show several methods for doing this. The class MUST inherit from a certain interface, ImplementMe which has a special method called runMe(). So here's what I tried:

ImplmentMe a =
   (ImplementMe) ImplementMe.class
                   .getClassLoader()
                   .loadClass("my.package.IImplementedYou")
                   .newInstance();
a.runMe();

It works, but it's so ugly. I at least expected not needing a cast. Please tell me there is a better way.

3
  • 2
    You'll need the cast regardless. The compiler can't automatically promote Object to ImplementMe. Commented Feb 1, 2011 at 16:32
  • That's so odd. I thought by using ImplementMe.class.getClassLoader(), it would take the hint that the class must inherit from ImplementMe..oh well. Commented Feb 1, 2011 at 16:57
  • Class has a asSubclass method. So ...loadClass(...).asSubclass(ImplementMe.class).newInstance() will return an ImplementMe instance (if it succeeds). Commented Aug 30, 2017 at 13:43

9 Answers 9

39

No, there is no better way (by design). You are not supposed to do this, Java is designed as a type-safe language. However, I can understand that you sometimes need to do things like this, and for that purposes you can create a library function like this:

public <T> T instantiate(final String className, final Class<T> type){
    try{
        return type.cast(Class.forName(className).newInstance());
    } catch(InstantiationException
          | IllegalAccessException
          | ClassNotFoundException e){
        throw new IllegalStateException(e);
    }
}

Now your client code can at least call this method without casting:

MyInterface thingy =
    instantiate("com.foo.bar.MyInterfaceImpl", MyInterface.class);
Sign up to request clarification or add additional context in comments.

1 Comment

You need no unchecked cast. Just use return type.cast(Class.forName(className).newInstance());. The advantage is that it throws immediately (it's more fail-fast).
14

Try Class.forName("my.package.IImplementedYou").

1 Comment

You might note that he'll still need the cast.
8

Here's how I would do it:

ImplementMe noCastNeeded = 
    this.getClassLoader()
        .loadClass("my.package.IImplementedYou")
        .asSubclass(ImplementMe.class).newInstance();

There are some Exceptions to catch but that's ok I think. :)

Comments

2

In all essence that is what will happen regardless of whether you're using a third party toolkit for it or not. Casting the object will inherently be mandatory unless expecting an Object. You can however make a routine which does that for you:

public <T> T instantiateObject(String name, Class<T> cls) throws Exception {
    return (T) Class.forName(name).newInstance();
}

Which you can use:

AClass cls = instantiateObject("com.class.AClass", AClass.class);

But if you come this far, the String name is actually redundant (given AClass is a concrete class). You might as well:

public <T> T instantiateObject(Class<T> cls) throws Exception {
    return (T) Class.forName(cls.getCanonicalName()).newInstance();
}

Which you can use:

AClass cls = instantiateObject(AClass.class);

2 Comments

(T) Class.forName(cls.getCanonicalName()).newInstance(); why so complicated? how about cls.newInstance(); ? But in my interpretation, the interface class is loaded, but not the implementation class
True, cls.newInstance() would be shorter. Also using the second approach it has to be a concrete class.
1

You can shorten it a bit like

ImplementMe a = (ImplementMe) Class
                               .forName("my.package.IImplementedYou")
                               .newInstance();

but you can't get rid of the cast. There may be a way to avoid the cast, but only if you can avoid the subproblem of loading class by name.

Comments

1

The alternative is to use forName, but it does not get much better than what you currently have:

ImplementMe a = 
    (ImplementMe) Class.forName("my.package.IImplementedYou").newInstance();
a.runMe();

Indeed, forName will use getClassLoader().loadClass() behind the scenes to load the class, as you can see in the source code of Class.java.

Comments

0

You will need a cast, because the compiler cannot tell from the code that the object is of type ImplementMe. It thus requires the programmer to issue a cast, which will throw a ClassCastException if the object is not an ImplementMe instance.

Comments

0

What you have may work, but you don't have to load the class using the same classloader that loaded ImplementMe. This should work equally well:

Object newInstance = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou").newInstance();

The important thing is that the classloader knows both the class file with the implementation of "my.package.IImplementedYou" and the class with the implementation of "ImplementMe".

You may explicitly check that IImplementedYou really implements ImplementMe like this:

if(newInstance instanceof my.package.IImplementedYou) {  
    ((ImplementMe)newInstance).runMe(); 
}

You may also check that IImlementedYou really is implementing the interface before creating the instance:

Class c = this.getClass().getClassLoader().loadClass("my.package.IImplementedYou");
if(ImplementMe.class.isAssignableFrom(c)) {
    Object newInstance = c.newInstance(); 
}

Comments

-1
(MyInterface)Class.forName(className).newInstance()

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.