Why does .getConstructor fail with a NoSuchMethodException despite zero-arg constructors existing for the class?

I’m programming a game for a programming course in college, and I’ve run into some trouble with the .getConstructor method. What I’m trying to do is pass a class that has to be a subclass of another class as an argument into a method and create a new instance of that class there to do some other stuff with it. This is the code for the method:

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;

class PlayerCharacter extends GameCharacter
{
  
  // other methods...
   
  class Inventory {
    
    // constructor

    void add(Class<? extends Ingredient> ingredientClass) {
      try {
        Constructor<? extends Ingredient> ingredientConstructor = ingredientClass.getConstructor();
        ingredientConstructor.setAccessible(true);
        Ingredient ingredient = ingredientConstructor.newInstance();
        
        // do stuff with ingredient
        
      }
      catch (NoSuchMethodException e) {
        e.printStackTrace();
        println("Constructor not available: " + e.getMessage());
      }
      catch (IllegalAccessException e) {
        println("Constructor can not be accessed: " + e.getMessage());
      }
      catch (InvocationTargetException e) {
        println("Exception in constructor: " + e.getCause().getMessage());
      }
      catch (InstantiationException e) {
        println("Class cannot be instantiated: " + e.getMessage());
      }
    }
  }
}

When running this though, I get the following exception:

java.lang.NoSuchMethodException: ice_creamillionaire$VanillaIceCream.<init>()
    at java.base/java.lang.Class.getConstructorO(Class.java:3585) 
    at java.base/java.lang.Class.getConstructor(Class.java:2271) 
    at ice_creamillionaire$PlayerCharacter$Inventory.add(ice_creamillionaire.java:800) 
    at ice_creamillionaire$PlayerCharacter.interact(ice_creamillionaire.java:769)
    at ice_creamillionaire.keyPressed(ice_creamillionaire.java:55)
    at processing.core.PApplet.keyPressed(PApplet.java:2772)
    at processing.core.PApplet.handleKeyEvent(PApplet.java:2636)
    at processing.core.PApplet.dequeueEvents(PApplet.java:2262)
    at processing.core.PApplet.handleDraw(PApplet.java:2104)
    at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1388)
    at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356) 
Constructor not available: ice_creamillionaire$VanillaIceCream.<init>() 

All the classes involved here (the subclass that is passed as an argument and all of its superclasses) have zero-argument constructors, though:

class Ingredient {
    PImage sprite;
    Class[] validBases;

    Ingredient() {
    }   
  
    // other methods
}
    
class IceCream extends Ingredient {
    Class[] validBases = new Class[] {Waffle.class};

    IceCream() {
    }
}
    
class VanillaIceCream extends IceCream {
    PImage sprite = loadImage(IngredientSpriteRoot + "vanilla.png");

    VanillaIceCream() {
    }
}

I’ve done quite a bit of research on how .getConstructor should work, including the Java Documentation and topics that seem to handle similar code structures in Java. I’ve also tried .getDeclaredConstructor, but got the same exception. Following the research, I’ve also tried to make the constructors accessible using the public keyword, but that didn’t work either. My assumption is at this point that this might just not work in Processing due to all the code being wrapped in the PApplet, or (more likely), that I just missed something about how to use .getConstructor correctly. So I’d like to ask if someone can help me figure out what is going wrong here. I’d be very grateful for any help with this.

Note: this question, shortened due to platform restrictions, has also been posted by me to the #code-review channel of the Pirate Software Discord server.

m missing here

:wink:

Chrisir

2 Likes
2 Likes

I have a class


// ===========================================================
// That's the basic class for all elements

abstract class ElectricElement {

....

Then I have a few classes such as


// =========================================================

class Motor extends ElectricElement {
  // Motor

 

or


// =======================================

class Lamp extends ElectricElement {


Get class name

Now inside abstract class ElectricElement I can get the classes like “Lamp” with getClass().toString()

(and that does not really help you)

1 Like

Processing wraps your sketch code inside a class named after the sketch so all your classes are inner classes. To instantiate an inner class using reflection requires an instance of the enclosing class.

The easiest solution is to make all your classes top-level by declaring them public static but it will mean they cannot directly access variables declared ‘globally’ in the main sketch. You would have to pass them as parameters to the class methods, which is good practice anyway.

So I have created a sketch based on my comments that should help.

import java.lang.reflect.*;
import java.util.*;

GameCharacter player;

public void setup() {
  size(400, 300);
  player = new PlayerCharacter();
  player.addToInventory(VanillaIceCream.class);
  player.addToInventory(IceCream.class);
  // List players inventory
  player.printInventory();
}

public static class GameCharacter {
  protected Inventory invt = new Inventory();
  public GameCharacter() {
  }
  public void addToInventory(Class<? extends Ingredient> type) {
    invt.add(type);
  }
  public void printInventory() {
    invt.printInventory();
  }
}

public static class PlayerCharacter extends GameCharacter {
  public PlayerCharacter() {
  }
}

public static class Inventory {
  protected List<Ingredient> items = new ArrayList<Ingredient>();

  public Inventory() {
  }

  public void add(Class<? extends Ingredient> type) {
    try {
      Constructor<? extends Ingredient> cstr = type.getDeclaredConstructor();
      Ingredient ingredient = cstr.newInstance();
      items.add(ingredient);
      // do stuff with ingredient
    }
    catch (NoSuchMethodException e) {
      e.printStackTrace();
      println("Constructor not available: " + e.getMessage());
    }
    catch (IllegalAccessException e) {
      println("Constructor can not be accessed: " + e.getMessage());
    }
    catch (InvocationTargetException e) {
      println("Exception in constructor: " + e.getCause().getMessage());
    }
    catch (InstantiationException e) {
      println("Class cannot be instantiated: " + e.getMessage());
    }
  }

  public void printInventory() {
    for (Ingredient item : items)
      item.print();
  }
}

public static class Ingredient {
  public Ingredient() {
  }
  public void print() {
    println("Ingredient");
  }
}

public static class IceCream extends Ingredient {
  IceCream() {
  }
  public void print() {
    println("Icecream");
  }
}

public static class VanillaIceCream extends IceCream {
  VanillaIceCream() {
  }
  public void print() {
    println("Vanilla Icecream");
  }
}
2 Likes

Thank you for linking me to those two topics - I’ve read them carefully and with some more research, I understood why I have to use .getDeclaredConstructor and give it the sketch’s class as an argument.

So now, following your implementation from this topic: How do you choose what class you add to a list with a variable?, I had added this to the beginning of my sketch go get its class:

final Class<? extends PApplet> sketchClass = getClass();

and changed the code in the method to

Ingredient ingredient = ingredientClass.getDeclaredConstructor(sketchClass).newInstance(this);

That got me an IllegalArgumentException: argument type mismatch, so I printed the stack trace of the Exception and got this:

at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) 
at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) 
at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) 
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) 
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) 
at ice_creamillionaire$PlayerCharacter$Inventory.add(ice_creamillionaire.java:802) 
at ice_creamillionaire$PlayerCharacter.interact(ice_creamillionaire.java:771) 
at ice_creamillionaire.keyPressed(ice_creamillionaire.java:55) 
at processing.core.PApplet.keyPressed(PApplet.java:2772) 
at processing.core.PApplet.handlekeyEvent(PApplet.java:2636) 
at processing.core.PApplet.dequeueEvents(PApplet.java:2262) 
at processing.core.PApplet.handleDraw(PApplet.java:2104) 
at processing.awt.PSurfaceAWT$9.callDraw(PSurfaceAWT.java:1388) 
at processing.core.PSurfaceNone$AnimationThread.run(PSurfaceNone.java:356) 

I checked the documentation of newInstance to see what the problem could be, and I read that the parameters of newInstance are

an array of objects to be passed as arguments to the constructor call

Now knowing that, I thought that because I was calling zero-argument constructors, I should try calling newInstance without any arguments. I did that, but it got me an IllegalArgumentException: wrong number of arguments with the same stack trace. After that, I looked at the code in your implementations again, but I can’t figure out what I’m doing wrong, and I’d be very thankful for some more help.

Thanks, that sounds like a good solution, but when I’ve tried working with static classes and methods in the past (and also just now when I tried it out again), the IDE always complains that I can’t access inbuilt Processing functions in those classes and methods any more, as the methods from PApplet aren’t static. For example:

Cannot make a static reference to the non-static method loadImage(String) from the type PApplet

Is there something I’m missing to make this work? Because I’d love to work with static anyway, but just thought I couldn’t in Processing.

OK the version below works with inner classes so should solve the creation problem and access to Processing methods.

import java.lang.reflect.*;
import java.util.*;

GameCharacter player;

public void setup() {
  size(400, 300);
  player = new PlayerCharacter();
  player.addToInventory(this, VanillaIceCream.class);
  player.addToInventory(this, IceCream.class);
  // List players inventory
  player.printInventory();
}

public class GameCharacter {
  protected Inventory invt = new Inventory();
  public GameCharacter() {
  }
  public void addToInventory(PApplet papp, Class<? extends Ingredient> type) {
    invt.add(papp, type);
  }
  public void printInventory() {
    invt.printInventory();
  }
}

public class PlayerCharacter extends GameCharacter {

  public PlayerCharacter() {
  }
  // other methods...
}

public class Inventory {
  protected List<Ingredient> items = new ArrayList<Ingredient>();

  public Inventory() {
  }

  public void add(PApplet papp, Class<? extends Ingredient> type) {
    try {
      Ingredient ingredient = null;
      Class<?>[] innerClasses = papp.getClass().getDeclaredClasses();
      for (Class<?> innerClass : innerClasses) {
        if (innerClass == type) {
          Constructor<? extends Ingredient> cstr = type.getDeclaredConstructor(papp.getClass());
          ingredient = cstr.newInstance(papp);
          break;
        }
      }
      // do stuff with ingredient
      items.add(ingredient);
    }
    catch (NoSuchMethodException e) {
      e.printStackTrace();
      println("Constructor not available: " + e.getMessage());
    }
    catch (IllegalAccessException e) {
      println("Constructor can not be accessed: " + e.getMessage());
    }
    catch (InvocationTargetException e) {
      println("Exception in constructor: " + e.getCause().getMessage());
    }
    catch (InstantiationException e) {
      println("Class cannot be instantiated: " + e.getMessage());
    }
  }

  public void printInventory() {
    for (Ingredient item : items)
      item.print();
  }
}

public class Ingredient {
  public Ingredient() {
  }
  public void print() {
    println("Ingredient");
  }
  // other methods
}

public class IceCream extends Ingredient {
  IceCream() {
  }
  public void print() {
    println("Icecream");
  }
}

public class VanillaIceCream extends IceCream {
  VanillaIceCream() {
  }
  public void print() {
    println("Vanilla Icecream");
  }
}
3 Likes

Java’s keyword this represents the current class’ code block type.

Your newInstance(this) expects you to pass a PApplet type.

But b/c you’re inside a class block, its type is of the same of the class that block belongs to!

2 Likes

Thanks so much, that solved it. I now also added

final PApplet sketch = this;

to the top and changed the method code to

Ingredient ingredient = ingredientClass.getDeclaredConstructor(sketchClass).newInstance(sketch);

and finally it works! Thanks a lot to you too @quark, you already got me on the right track with the last post of yours.

1 Like

Although reflection/introspection is a powerful workaround tool, we should avoid abusing it.

You could easily replace newInstance() w/ a switch/case block, where each “className” has a corresponding return new className();.

2 Likes