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.
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");
}
}
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.
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.