Matt McHenry
Matt McHenry

Reputation: 20909

Which java.lang.Class method generates the right input for Class.forName()?

I would like to write some code like this:

Object o = ...;
String oTypeName = o.getClass().getName();

//on the other side of the wire:

Class<?> oClass = Class.forName(oTypeName);
Object oAgain = oClass.newInstance();

However, it's not clear from the javadoc which method I should use to initialize oTypeName, i.e. which method will produce the expected input to Class.forName():

It's fairly obvious that I don't want either of these:

I don't expect this to work for primitive types. It's okay if it won't work for arrays. The main thing I'm concerned about is nested classes and Foo.Bar vs. Foo$Bar.

Upvotes: 19

Views: 1874

Answers (4)

Karthik Cherala
Karthik Cherala

Reputation: 322

Object oAgain = oClass.newInstance();

[EDIT] No Matter which method(getName(), getCanonicalName(), etc..) you cannot use newInstance() method to create an object for a non static inner class

If you are creating an object using newInstance(), then it is mandatory that the underlying class contains a no arg constructor. Even if we explicitly insert one no argument constructor, the compiler will convert it to a constructor with some arguments(Only in case of a non static inner class)

[END EDIT]

Below is the link for brief code that i found. It demonstrates the explanation above.

http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

Upvotes: -1

Tunaki
Tunaki

Reputation: 137064

The definite answer is getName(). Although a bit hidden, this is specified in the Javadoc of the overload of forName(className, initialize, loader):

Given the fully qualified name for a class or interface (in the same format returned by getName) this method attempts to locate, load, and link the class or interface.

And it is also specified that calling forName(className) is equivalent to calling this overload, with default values:

Invoking this method is equivalent to:

Class.forName(className, true, currentLoader) 

where currentLoader denotes the defining class loader of the current class.


Here's a sample code showing that it works for nested classes, local classes, anonymous class, primitive or object arrays. It won't work for primitives because Class.forName doesn't handle primitive classes.

public class Main {
    public static void main(String... args) throws ClassNotFoundException {
        class LocalClass {}

        System.out.println(Class.forName(name(StaticNestedClass.class))); //static nested class
        System.out.println(Class.forName(name(InnerClass.class))); // inner class
        System.out.println(Class.forName(name(Integer[].class))); // object array
        System.out.println(Class.forName(name(int[].class))); // primitive array
        System.out.println(Class.forName(name(List.class))); // interface
        System.out.println(Class.forName(name(LocalClass.class))); // local class
        System.out.println(Class.forName(name(new Object(){}.getClass()))); // anonymous class
    }

    private static String name(Class<?> clazz) {
        return clazz.getName();
    }

    public static class StaticNestedClass {}
    public class InnerClass {}
}

Upvotes: 15

d3n13d1
d3n13d1

Reputation: 300

I always use getCanonicalName() Internal objects (like your Foo$Bar If static public vs inline implementation) will be able to be constructed as well.

Also you can make it work w/ primitives .. 'int.class' for example does exist. However you'll probably have to do a check on the primitives classes, and make the Object instance (Integer vs int) then call the accessor like intValue(). Because of this i use a lot of Object instances vs primitive, but that's just my preference I guess.

Upvotes: 1

Matt McHenry
Matt McHenry

Reputation: 20909

It looks like either getName() or getTypeName() works, at least in the simple case:

public final class ForNameTest{

    public static void main(String[] args) throws Exception{
        Object o = new Foo();
        System.out.println("class is: " + o.getClass());

        for(String getterMethodName : Arrays.asList("getName", "getTypeName", "getCanonicalName")){
            Method m = Class.class.getMethod(getterMethodName);
            String oTypeName = m.invoke(o.getClass()).toString();
            System.out.println(getterMethodName + " yields " + oTypeName);
            try{
                Class<?> oType = Class.forName(oTypeName);
                Object oAgain = oType.newInstance();
                System.out.println(" ... and it works: " + oAgain);
            } catch (Exception e){
                System.err.println(" ... and it fails: " + e);
            }
        }
    }

    public static class Foo{}
}

The output produced is:

class is: class ForNameTest$Foo
getName yields ForNameTest$Foo
 ... and it works: ForNameTest$Foo@4554617c
getTypeName yields ForNameTest$Foo
 ... and it works: ForNameTest$Foo@74a14482
getCanonicalName yields ForNameTest.Foo
 ... and it fails: java.lang.ClassNotFoundException: ForNameTest.Foo

Upvotes: 2

Related Questions