[SOLVED] Instantiating a particular class constructor using reflection

Hello again, oh great Java gurus! :star_struck:

I find myself having to instantiate a class from a string using one of its particular constructors. The api for getDeclaredConstructors() indicates this is possible, but the examples I’ve found online of its implementation aren’t clicking for me.

Here’s a stripped-down example of what I’m hoping to achieve.

import java.lang.reflect.*;

// This PApplet processing session //
final String THISPROC = this.getClass().getCanonicalName();
final PApplet PAPPLET = this;

AbstractExp ae;
Wallpaper wp;
String pattern = "pg";

void setup(){
  
  ae = new AbstractExp(pattern);
  ae.printPattern();
  
  wp = new Wallpaper("AbstractExp", pattern);
  
}

class Contents {
  String pattern;
  
  Contents(){}
  
  Contents(String pattern_){
    pattern = pattern_;
  }
  
  void printPattern(){ print("pattern is",pattern); }
  
}

class AbstractExp extends Contents {
  
  AbstractExp(){}
  
  AbstractExp(String pattern_){
    super(pattern_);
  }
  
  void printPattern(){
    super.printPattern();
    println(" for AbstractExp"); 
  }
  
}

class Wallpaper {
  
  Contents contents;
  
  Wallpaper(String design, String pattern){
    
    println("Wallpaper: celldesign:",design,"pattern:",pattern);
    
    contents = null;

    try {
      Class<?> innerClass = Class.forName(THISPROC + "$" + design);
      // Q: How do I call AbstractExp's (String){} constructor with the getDeclaredConstructor method?
      // https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getDeclaredConstructor-java.lang.Class...-
      Constructor<?> ctor = innerClass.getDeclaredConstructor( PAPPLET.getClass() );
      contents = (Contents) ctor.newInstance(PAPPLET); 
      println("Wallpaper: class:",design.getClass());
    } 
    catch (ClassNotFoundException e) {
      e.printStackTrace();
    } 
    catch (NoSuchMethodException e) {
      e.printStackTrace();
    } 
    catch (InvocationTargetException e) {
      e.printStackTrace();
    } 
    catch (InstantiationException e) {
      e.printStackTrace();
    } 
    catch (IllegalAccessException e) {
      e.printStackTrace();
    }  
  }
}

And, obviously, I’d like to call innerClass.getDeclaredConstructor( PAPPLET.getClass() );
with my String arg. (This is discussed here.)

Thank you for reading!

1 Like

Woops… I must have hit something by accident. I don’t want to delete it. Please cancel the deletion.

Yes, I remember this conversation. But how does it answer my current question?

The old sketch only had an inner class called Inner w/ an empty constructor:

class Inner {
  @Override String toString() {
    return getClass().getName();
  }
}

However, your class now also got a constructor w/ a String parameter:

So you’re gonna need to request that when you invoke the method Class::getDeclaredConstructor():
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#getDeclaredConstructor(java.lang.Class...)

Plus, pass the String argument when invoking the method Constructor::newInstance():
Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/reflect/Constructor.html#newInstance(java.lang.Object...)

Here’s a more updated version of my old sketch I did for you back then:

/**
 * Reflexive Inner Class Instantiation (v3.0)
 * GoToLoop (2019/Jan/30)
 *
 * https://Discourse.Processing.org/t/
 * instantiating-a-particular-class-constructor-using-reflection/7968/6
 *
 * https://Forum.Processing.org/two/discussion/27164/
 * string-to-class-classnotfoundexception#Item_5
 */

void setup() {
  final Class<?> appCls = getClass(), innerCls = appCls.getDeclaredClasses()[0];
  println(innerCls);

  final Inner[] inners = new Inner[2];
  try {
    inners[0] = (Inner) innerCls.getDeclaredConstructor(appCls)
      .newInstance(this);

    inners[1] = (Inner) innerCls.getDeclaredConstructor(appCls, String.class)
      .newInstance(this, "String");
  }
  catch (final ReflectiveOperationException ex) {
    System.err.println(ex);
  }
  printArray(inners);

  exit();
}

class Inner {
  String msg = " Constructor";

  Inner() {
    msg = "Empty" + msg;
  }

  Inner(final String txt) {
    msg = txt + msg;
  }

  @Override String toString() {
    return getClass().getName() + TAB + TAB + msg;
  }
}
1 Like

Sorry, no, that was me deleting my reply - I’d misread something in your question and realised you’d already looked at what I was suggesting! :smile:

I’ve added your code as is (preserving my previous classes) and I get the error:
ClassCastException: sketch_19013a$AbstractExp cannot be cast to sketch_!90130a$Inner

final PApplet PAPPLET = this;

AbstractExp ae;
Wallpaper wp;
String pattern = "pg";

void setup(){
  
  //ae = new AbstractExp(pattern);
  //ae.printPattern();
  
  //wp = new Wallpaper("AbstractExp", pattern);
  
  Class<?> appCls = getClass();
  Class<?> innerCls = appCls.getDeclaredClasses()[0];
  println("Inner Cls:",innerCls);

  Inner[] inners = new Inner[2];
  try {
    inners[0] = (Inner) innerCls.getDeclaredConstructor(appCls).newInstance(this);

    inners[1] = (Inner) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(this, "String");
  }
  catch (final ReflectiveOperationException ex) {
    System.err.println(ex);
  }
  printArray(inners);
  
}

Update: Actually, I think I’m realizing that I need to instantiate the appCLs with the string name of the class: eg: Class<?> appCls = Class.forName(THISPROC + "$" + "AbstractExp");

Trying that right now…

…getting closer…

Now I get an ArrayIndexOutOfBoundsException: 0 for the innerCls instantiation.

  try {
    Class<?> appCls = Class.forName(THISPROC + "$" + "AbstractExp");
    println("app CLs:",appCls);

    Class<?> innerCls = appCls.getDeclaredClasses()[0];
    println("Inner Cls:",innerCls);
  
    Inner[] inners = new Inner[2];
    try {
      inners[0] = (Inner) innerCls.getDeclaredConstructor(appCls).newInstance(this);
  
      inners[1] = (Inner) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(this, "String");
    }
    catch (final ReflectiveOperationException ex) {
      System.err.println(ex);
    }
    printArray(inners); 
     
  } 
  catch (ClassNotFoundException e) {
    e.printStackTrace();
  } 
1 Like

The (cast_type) operator gotta be of a datatype compatible w/ the class you’re attempting to instantiate w/ Constructor::newInstance(). :face_with_monocle:

BtW, here’s version 3.1. It’s now using Class.forName() in place of Class::getDeclaredClasses(): :innocent:

/**
 * Reflexive Inner Class Instantiation (v3.1)
 * GoToLoop (2019/Jan/30)
 *
 * https://Discourse.Processing.org/t/
 * instantiating-a-particular-class-constructor-using-reflection/7968/9
 *
 * https://Forum.Processing.org/two/discussion/27164/
 * string-to-class-classnotfoundexception#Item_5
 */

void setup() {
  final Class<?> appCls = getClass(), innerCls;
  try {
    innerCls = Class.forName(appCls.getName() + '$' + "Inner");
  }
  catch (final ClassNotFoundException ex) {
    throw new RuntimeException(ex);
  }
  println(innerCls, ENTER);

  final Inner[] inners = new Inner[2];
  try {
    inners[0] = (Inner) innerCls.getDeclaredConstructor(appCls)
      .newInstance(this);

    inners[1] = (Inner) innerCls.getDeclaredConstructor(appCls, String.class)
      .newInstance(this, "String");
  }
  catch (final ReflectiveOperationException ex) {
    System.err.println(ex);
  }
  printArray(inners);

  exit();
}

class Inner {
  String msg = " Constructor";

  Inner() {
    msg = "Empty" + msg;
  }

  Inner(final String txt) {
    msg = txt + msg;
  }

  @Override String toString() {
    return getClass().getName() + TAB + TAB + msg;
  }
}

I know you’re doing your best to help me understand, but it’s not coming through.

Where is appCls coming from? Is that comparable to what I’m using: final PApplet PAPPLET = this;?

If I execute your latest code I get the Inner class with an empty and a string constructor. Great.

But when I replace “Inner” with “AbstractExp” – which I assume is what I need to do, since it’s the String name of the class I want to instantiate – I get the ClassCastException again.

There’s a step I’m not understanding… whether it’s due to my own limitations or your pedagogical style or a combination of both I can’t answer…

Well, it’s initialized from this statement: final Class<?> appCls = getClass();

Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Object.html#getClass()

It means it’s the class of our app sketch instance. More precisely, a subclass of PApplet.

RIght. appCls is the variable that’s declared. But is it this process (ie: this session) that it’s getting? I’m assuming it is since it’s able to find the Inner classes.

But you didn’t answer my question about how to actually use the String name of the class that I ultimately want to instantiate: AbstractExp. Where/how do i do that?

In my own sketch example, I’ve only got 1 inner class called Inner.
I get its Class by running the try {} block below calling Class.forName():

Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#forName(java.lang.String)

  try {
    innerCls = Class.forName(appCls.getName() + '$' + "Inner");
  }

For class AbstractExp, simply replace the String “Inner” w/ “AbstractExp”.

That’s what I’ve been doing. And, as I’ve said, I get the exception ClassCastException: sketch_19013a$AbstractExp cannot be cast to sketch_!90130a$Inner

I’ve already stated about the need of the (cast_type) to be compatible w/ the datatype of the class you’re attempting to instantiate:

Would you be so kind as to show me how to do that, please?

I guess that’d be as easy as replacing all the Inner types in my sketch w/ AbstractExp: :thinking:

void setup() {
  final Class<?> appCls = getClass(), innerCls;
  try {
    innerCls = Class.forName(appCls.getName() + '$' + "AbstractExp"); // replaced
  }
  catch (final ClassNotFoundException ex) {
    throw new RuntimeException(ex);
  }
  println(innerCls, ENTER);

  final AbstractExp[] inners = new AbstractExp[2]; // replaced
  try {
    inners[0] = (AbstractExp) innerCls.getDeclaredConstructor(appCls) // replaced
      .newInstance(this);

    inners[1] = (AbstractExp) innerCls.getDeclaredConstructor(appCls, String.class) // replaced
      .newInstance(this, "String");
  }
  catch (final ReflectiveOperationException ex) {
    System.err.println(ex);
  }
  printArray(inners);

  exit();
}

class AbstractExp { // replaced
  String msg = " Constructor";

  AbstractExp() { // replaced
    msg = "Empty" + msg;
  }

  AbstractExp(final String txt) { // replaced
    msg = txt + msg;
  }

  @Override String toString() {
    return getClass().getName() + TAB + TAB + msg;
  }
}
2 Likes

D’OH! (Once again…)

Yes, now it’s obvious. Sorry I was so thick…

Thank you for sticking with me and helping me out.

1 Like

Just for the sake of completion, this is the final bit of code I’ll add to my original Wallpaper class:

  Contents c = null;
  
  final Class<?> appCls = getClass(), innerCls;
  try {
    innerCls = Class.forName(appCls.getName() + '$' + "AbstractExp"); // or whatever child class it is
    c = (Contents) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(this, "Booyah");
  }
  catch (final ClassNotFoundException ex) {
    throw new RuntimeException(ex);
  }
  catch (final ReflectiveOperationException ex) {
    System.err.println(ex);
  } 
  c.printPattern(); 

Just a lil´ tip: No need for a catch () {} block w/ ClassNotFoundException if there’s already 1 w/ ReflectiveOperationException. :wink:

ReflectiveOperationException is the parent class for all reflection exceptions: :bug:

Docs.Oracle.com/en/java/javase/11/docs/api/java.base/java/lang/ReflectiveOperationException.html

1 Like

Actually, I ran into another problem.

Everything works fine if that code stays in setup().

But when I incorporate that bit of code into my Wallpaper class I get a classNotFound exception: sketch_190130a$Wallpaper$AbstractExp.

Of course, because in the cmd Class<?> appCls = getClass(), innerCls; getClass() refers to the WallPaper class.

But if I change the line to Class<?> appCls = PAPPLET.getClass(), innerCls; I get an argument type mismatch with the command
contents = (Contents) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(this, "Test");

So how do I fix this?

Update: Actually, I just figured it out.

contents = (Contents) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(PAPPLET, "Booyah");

I need the class as the first parm in the newInstance method… Makes sense…

Just for posterity, here’s the final Wallpaper class code:

class Wallpaper {
  
  Contents contents;
  
  Wallpaper(String design, String pattern){
    
    println("Wallpaper: celldesign:",design,"pattern:",pattern);
    
    contents = null;
    
    final Class<?> appCls = PAPPLET.getClass(), innerCls;
    try {
      innerCls = Class.forName(appCls.getName() + '$' + design);
      contents = (Contents) innerCls.getDeclaredConstructor(appCls, String.class).newInstance(PAPPLET, pattern);
    }
    catch (final ReflectiveOperationException ex) {
      //System.err.println(ex);     
      throw new RuntimeException(ex);
    } 

    // just for dev testing
    println("hello");
    contents.printPattern(); 
  }
}

Thanks again, GoToLoop, for your help.

2 Likes