rd1346
rd1346

Reputation: 23

Java Reflection: how can you get constructor of a class you don't know?

Let's say you're making a general reflection class. You're given the name of the class in string format: So you do Class.forName(class_name) to get the class name.

Next step would be to get the constructor and you can do Class.forName(class_name).getConstructor(null) assuming that there isn't any args.

How do you now get an instance of this? You would have to downcast but what do you downcast to? class_name name = (what goes here) Class.forName(class_name).getConstructor(null).newInstance(null)

I tried to put in the class_name in the parenthesis class_name name = (class_name) Class.forName(class_name).getConstructor(null).newInstance(null) but getting an error. Well not compiling.

Update: Only thing known is that the constructor doesn't have any args, and instance doesn't have parameters. So you can pass null to both.

Upvotes: 0

Views: 4938

Answers (2)

Matteo NNZ
Matteo NNZ

Reputation: 12665

What you're asking for is impossible.

If you load the class using Class.forName(someClassName), it means you do not know at compile time what the class will be, you will only know it at runtime (when the value of someClassName will be defined).

That means, you can't code based on that specific type (because if you want to have typed code at compile time, then you should know the exact type at compile time and that's not your case).

However, even though you can only declare your instance as Object obj = ... instead of SomeClassName obj = ... (which I understand is what you wanted to do), be reassured that your obj will actually be an instance of SomeClassName at runtime, even if the compiler doesn't know it yet.

If you add more details about what you're trying to do (I mean why do you need to cast the reflected instance to the specific type), then I can complete my answer to address your exact concern.

EDIT To answer to the questions in the comments:

so inherently Java knows that it's a Foo class but it'll just return a generic Object because it wants to be sure that it's correct?

No. When coding there are two phases: compile-time and run-time. The compile-time is the moment when the code is compiled into an executable. The run-time is the moment when the code is executed.

Take the initial expression of your question:

Object obj = Class.forName(class_name).getConstructor().newInstance();

When does the variable class_name take a concrete value? Well, that's at runtime (when the code runs).

That means, there is simply no way that the compiler can ever know what the return of this instruction will be, not until the time the code is executing, before it can only say that it will be an Object (because in Java, everything is an Object).

But again, at runtime, if you're passing the name of the class Foo, the the instance will be a Foo (assuming of course that the reflective operation works correctly).

the only time you cast is when you know what class your dealing with. But then what is the point of ever casting it?

That's correct, you can only cast when you know what to cast into. And there is a sure point about casting.

Let's take a very simple example. Imagine that you have a String object, like String str = "something that is a string";. Knowing that str is a String, you can call methods that belong to the class String, for example:

str.split(" "); //<-- valid because str is a String
str.replace("o", "c"); //<-- valid because str is a String

Now, imagine that str is a String, but it's declared as Object:

Object str = "something that is a string";

Even though str is concretely a String, you won't be able to use that code anymore:

str.split(" "); //<-- INVALID: the class Object doesn't have a method .split()
str.replace("o", "c"); //<-- INVALID: the class Object doesn't have a method .replace() 

So if that was the case, you would cast str into a String and you would be able to do what you were doing:

Object str = "something that is a string";
String str2 = (String) str;
str2.split(...);
str2.replace(...);
etc.

And of course, the cast is only possible when Object str is actually a String, if it was (for example) an Integer, that would have raised a ClassCastException.

So why do you need to cast? Because at compile time, you need to write code against a specific object, and for that you need to cast to access the methods and properties correctly.

Upvotes: 2

Thomas Fritsch
Thomas Fritsch

Reputation: 10127

The classes Class and Constructor provide everything you need to accomplish your goal.

  1. From Class.forName(String) you get the Class object.
  2. By calling its getConstructors() method you get an array of Constructor objects (representing the declarations of the constructors).
    See the javadoc of Class.getConstructors().
  3. Then by calling the getParameterTypes() method of any of these Constructors you get an array of Class objects (the types of the formal parameters of a constructor).
    See the javadoc of Constructor.getParameterTypes().
  4. Now you know what types of parameters any constructor needs. Then you can actually invoke the constructor with such parameters by calling constructor.newInstance(param1, param2, ...).

Example: with this code

Class<?> clazz = Class.forName("java.util.HashMap");
System.out.println(clazz);
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> constructor : constructors) {
    System.out.println("  constructor: " + constructor);
    Class<?>[] parameterTypes = constructor.getParameterTypes();
    for (Class<?> parameterType : parameterTypes) {
        System.out.println("    parameterType: " + parameterType);
    }
}

you get all constructors of java.util.HashMap and their parameter types:

class java.util.HashMap
  constructor: public java.util.HashMap(int)
    parameterType: int
  constructor: public java.util.HashMap(int,float)
    parameterType: int
    parameterType: float
  constructor: public java.util.HashMap(java.util.Map)
    parameterType: interface java.util.Map
  constructor: public java.util.HashMap()

Upvotes: 0

Related Questions