javareflectionjavassist

How to instantiate a class created at runtime with javassist?


I've just started to use javassist and I can't seem to figure out how to instantiate a class made at runtime.

The makeNewClass() method makes the NewClass class like that:

public bin.objects.base.NewClass {
    public int quantity = 5;
    private float weight = 30.25f;

    public float getWeight() { return weight; }
    public void setWeight(float weight) { this.weight = weight; }
    public float totalWeight() { return quantity * getWeight(); }
}

This method works just fine:

public void makeNewClass() throws NotFoundException, IOException, CannotCompileException {
        // ClassMaker maker holds the CtClass object and handles all the "making"
        ClassMaker maker = new ClassMaker("bin.objects.base.NewClass");

        maker.addField(CtClass.intType, "quantity", "5", Modifier.PUBLIC);
        maker.addField(CtClass.floatType, "weight", "30.25f", Modifier.PRIVATE);

        maker.addMethod(Modifier.PUBLIC, CtClass.floatType, "totalWeight", null, null, 
                "{ return quantity * getWeight(); }", null, MethodType.standard);
        maker.getCtClass().writeFile();
    }

Now the problem begins. This method is supposed to instantiate the NewClass, access its fields, and call its methods.

    public void testNewClass() 
            throws Throwable {
        CtClass ctclass =  ClassPool.getDefault().get("bin.objects.base.NewClass");
        
        Object testClass = ctclass.toClass(new Loader(), null);
        
        // Throws NoSuchFieldException
        Field q = testClass.getClass().getDeclaredField("quantity");
        int quantity = (int) q.get(testClass);

        Class[] cArg = new Class[1];
        cArg[0] = Float.class;

        // Throws NoSuchMethodException
        Method m = testClass.getClass().getDeclaredMethod("getWeight", cArg);
        float weight = (float) m.invoke(testClass, null);

        // Throws NoSuchMethodException
        m = testClass.getClass().getDeclaredMethod("totalWeight", cArg);
        float totalWeight = (float) m.invoke(testClass, null);
        
        System.out.println("quantity = " + quantity +
                "weight = " + weight +
                "totalWeight = " + totalWeight);
    }

Now, I already figured out that testClass is actually initialized as an instance of java.lang.Class, not bin.objects.base.NewClass. So, obviously, it will not find the fields and methods of NewClass.

The question is: how do I solve that? I tried using the java.lang.Class.cast() method, but had no success.


Solution

  • I was finally able to find out what was the problem.

    Aparently, it was a problem with the ClassLoader. I've found the answer in this post. To solve it, I just included this line:

    Class newClasse = ctclass.toClass(this.getClass().getClassLoader(), 
        this.getClass().getProtectionDomain());
    

    @nullpointer's answer also helped.

    So, after some more changes, the testNewClass() method ended up like this:

    public void testNewClass() 
                throws Throwable {
            CtClass ctclass =  ClassPool.getDefault().get("bin.objects.base.NewClass");
            Class newClass = ctclass.toClass(this.getClass().getClassLoader(), 
                this.getClass().getProtectionDomain());
            Object objNewClass  = newClass.newInstance();
    
            System.out.println("Accessing the field 'public int quantity'..."); 
            Field q = newClass.getDeclaredField("quantity");        
            int quantity = (int) q.get(objNewClass);
            System.out.println("quantity = " + quantity);
    
            System.out.println("\nAccessing the field 'private float weight' " +
                "through the method 'public float getWeight()'...");
            Method m = newClass.getDeclaredMethod("getWeight", null);
            float weight = (float) m.invoke(objNewClass, null);
            System.out.println("weight = " + weight);
    
            System.out.println("\nAccessing the method 'public float totalWeight()'...");
            m = newClass.getDeclaredMethod("totalWeight", null);
            float totalWeight = (float) m.invoke(objNewClass, null);        
            System.out.println("totalWeight = " + totalWeight);
        }