'X.class' to get it's 'X' type?

2.2k views Asked by At

I have written simple container that registers a class and it's interface and has a method to create object from that information like this:

public class DIContainer {
    protected static DIContainer instance;
    protected Hashtable<Class<?>, Class<?>> classMap;

    protected DIContainer(){
        this.classMap = new Hashtable<Class<?>, Class<?>>();
    }

    public static DIContainer getInstance(){
        if (DIContainer.instance == null)
            DIContainer.instance = new DIContainer();
        return DIContainer.instance;
    }

    public void regClass(Class<?> interf, Class<?> classToReg){
        this.classMap.put(interf, classToReg);
    }

    public Object create(Class<?> interf) throws Exception{
        if(!this.classMap.containsKey(interf))
            throw new Exception("No such class registered with "+interf.getName()+" interface");
        return this.classMap.get(interf).newInstance();

    }
}

But I want before creating new instance to bypass it to proxy, for it to create, so I have this proxy class:

public class MyProxy implements InvocationHandler
{
  private Map map;
  private Object obj;
  public static Object newInstance(Map map, Object obj, Class[] interfaces)
  {
    return Proxy.newProxyInstance(map.getClass().getClassLoader(),
                                  interfaces,
                                  new MyProxy(map, obj));
  }
  public MyProxy(Map map, Object obj)
  {
    this.map = map;
    this.obj = obj;
  }
  public Object invoke(Object proxy, Method m, Object[] args) throws
Throwable
  {
    try {
      return m.invoke(obj, args);
    } catch (NoSuchMethodError e)
    { 
    //Object result;
    String methodName = m.getName();
    if (methodName.startsWith("get"))
    {
      String name = methodName.substring(methodName.indexOf("get")+3);
      return map.get(name);
    }
    else if (methodName.startsWith("set"))
    {
      String name = methodName.substring(methodName.indexOf("set")+3);
      map.put(name, args[0]);
      return null;
    }
    else if (methodName.startsWith("is"))
    {
      String name = methodName.substring(methodName.indexOf("is")+2);
      return(map.get(name));
    }
    return null;
  }
}

}

But for proxy class I need to provide type of class and it's interface, but I only have it's information with X.class. Can get the type (for example if it's class X) X, when I have X.class? Maybe I'm doing this the wrong way and I need to change something in order for it to work, but right now I figured I need to get that class type, so then I could provide it for proxy? Because if I would right something like this:

X.class x; I would get error. So I need to write like this X x;, but I only have X.class

Update:

To explain it simply, is it possible to get this:

X obj;

when you only have X.class (with X.class.newInstance it would instantiate it (like with new?), but I need not instantiated obj yet).

Update2

I tried this:

Object x = (Object) MyProxy.newInstance(map, this.classMap.get(interf).newInstance(), new Class[] {this.classMap.get(interf)});

But then I get this error:

Exception in thread "main" java.lang.IllegalArgumentException: class lab.X is not visible from class loader

My class X looks like this:

public class X implements I{
    String name;
    X(){}

    @Override
    public String getName() {
        return name;
    }

    @Override
    public void setName(String name) {
        this.name = name;
    }


}

and it's interface looks like this:

public interface I {
    public String getName();
    public void setName(String name);
}
3

There are 3 answers

4
Epiglottal Axolotl On

If I understand correctly, you are trying to instantiate an element of the class X.class? If that is the case, all you need to do is call X.class.newInstance().

0
eis On

java.lang.IllegalArgumentException: class lab.X is not visible from class loader

Isn't this error message quite clear? You need to make sure that the same class loader is being used.

In here:

public static Object newInstance(Map map, Object obj, Class[] interfaces)
{
    return Proxy.newProxyInstance(map.getClass().getClassLoader(),
                                  interfaces,
                                  new MyProxy(map, obj));
}

you shouldn't be using the class loader of the map, I'd think you should use class loader of the target object or pass the proper class loader as a separate argument.

I think there are other problems in your approach as well, such as not synchronizing your container creation and not using generics for your proxy type.

4
Joop Eggen On

For the first part, class DIContainer, it would be better to use:

protected final Map<Class<?>, Class<?>> classMap;

protected DIContainer() {
    classMap = Collections.synchronizedMap(new HashMap<Class<?>, Class<?>>());
}

public <I> void regClass(Class<I> interf, Class<? extends I> classToReg) {
    this.classMap.put(interf, classToReg);
}

public <T> T create(Class<T> interf) throws Exception {
    Class<?> implClass = classMap.get(interf);
    if (implClass == null) {
        throw new Exception("No such class registered with " + interf.getName()
            + " interface");
    }
    Constructor<?> c = implClass.getConstructor();
    c.setAccessible(true); // If default constructor not public
    return interf.cast(c.newInstance());
}
  • Safe typing, though still partly at run-time.

  • More or less obsolete Hashtable replaced by equivalent

  • Calling newInstance on the class bypasses exceptions thrown on getting the default constructor and doing that one's newInstance.

The second part of the question: I fail to understand it; the interface class(es) is what is proxied. In the create above you could easily proxy Class<T> and yield a (seemingly) T object. And you could delegate in the proxy class to an T object created as in the create above.