newInstance and ClassCastException

Started by
11 comments, last by xexuxjy 11 years, 1 month ago

I have an abstract class Entity and a derived class, let's call it Fred. I'm using newInstance() but I want to store the new instance in a HashMap<String, Entity>. When I do (Entity) klass.newInstance(); I'm getting an exception that I can't cast to Entity but I don't understand why.

To further complicate matters, the class definition for Fred is created dynamically at runtime using a Javassist class loader.

Any suggestions appreciated.

Advertisement

I don't suppose you can create a minimal example program demonstrating this behaviour?

I'm working on it but I don't know that I can. :)

Hopefully this is a small working example:


package cloud;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtMethod;
import javassist.CtNewMethod;
import javassist.Loader;
import javassist.Modifier;
import javassist.NotFoundException;

public class TestBuilder {

	private Loader classLoader;
	private ClassPool pool;
	private CtClass abstractEntityCtClass;

	public TestBuilder() {

	}

	public void buildClass(String name, String msg) {

		System.out.println("Building class " + name);

		// Create CtClass.
		CtClass ctClass = pool.makeClass(name);

		// Set superclass to cloud.Entity.
		try {
			ctClass.setSuperclass(abstractEntityCtClass);
		} catch (CannotCompileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}

		// Add field.
		String fieldName = "age";
		try {
			
			// Create field.
			CtField field = new CtField(CtClass.intType, fieldName, ctClass);
			field.setModifiers(Modifier.PUBLIC);
			ctClass.addField(field);
			
			String s = "public void say() { System.out.println(\"" + msg + "\");}";

			ctClass.addMethod(CtNewMethod.make(
					s, ctClass));

		} catch (CannotCompileException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return;
		}

		// Add property.
		String getterName = "getAge";
		String type = CtClass.intType.getName();

		StringBuffer sb = new StringBuffer();
		sb.append("public ").append(type).append(" ").append(getterName)
				.append("(){").append("return this.").append(fieldName)
				.append(";").append("}");

		try {
			CtMethod method = CtMethod.make(sb.toString(), ctClass);
			ctClass.addMethod(method);
		} catch (CannotCompileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return;
		}
		
		System.out.println("ctClass " + ctClass);
	}

	public Object createInstance(String name) {

		System.out.println("Creating class instance " + name);

		Object o = null;

		// Create and add to list.
		Class<? extends SimpleEntity> newKlass;
		try {
			newKlass = (Class<? extends SimpleEntity>) classLoader.loadClass(name);
			o = newKlass.newInstance();
			SimpleEntity e = (SimpleEntity)o;
			// objects.add(o);
			System.out.println("Created instance " + o);
		} catch (ClassNotFoundException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
			return o;
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return o;
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return o;
		}
		
		Method method;
		try {
			method = o.getClass().getMethod("say");
			method.invoke(o, null);
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		return o;

	}

	public void build() {

		String name = "foo.Bob";

		// Create new class pool and loader.
		buildPool();

		// Build the class.
		buildClass(name, "fred");

		// Create instance.
		Object o = createInstance(name);

		// Create new class pool and loader.
		buildPool();

		// Rebuild the class.
		buildClass(name, "bob");

		// Create an instance of the rebuilt class.
		Object o2 = createInstance(name);

	}
	
	private void buildPool() {

		// Create class loader and pool.
		this.pool = new ClassPool(ClassPool.getDefault());
		this.classLoader = new Loader();
		this.classLoader.setClassPool(this.pool);


		// Get the cloud.AbstractEntity class.
		try {
			abstractEntityCtClass = ClassPool.getDefault().get(
					"cloud.SimpleEntity");
			System.out.println("Abstract Entity " + abstractEntityCtClass);
		} catch (NotFoundException e) {
			e.printStackTrace();
			System.exit(-2);
		}

	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		TestBuilder builder = new TestBuilder();
		builder.build();

	}

}

Forgot the SimpleEntity class:


package cloud;

import java.io.Serializable;

public abstract class SimpleEntity implements Serializable {

}

I think my problems is because I'm using multiple class loaders though I'm not sure how to fix it yet.

Class loader for Entity sun.misc.Launcher$AppClassLoader@151cc2a8

Class loader for objcet javassist.Loader@9246bec

Can you set the AppClassLoader as the parent of the javassist's class loader ?

I tried changing to this:


this.classLoader = new Loader(ClassLoader.getSystemClassLoader(), this.pool);

But it still doesn't work.

Try this


this.classLoader = new Loader(getClass().getClassLoader(), this.pool); 

This topic is closed to new replies.

Advertisement