JDK-4401287 : (reflect) Class getConstructor(Class[]) doesn't work with abstract cls or intf
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 1.3.0
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: generic
  • CPU: generic
  • Submitted: 2001-01-03
  • Updated: 2012-09-28
  • Resolved: 2002-03-13
Related Reports
Duplicate :  
Relates :  
Description

Name: boT120536			Date: 01/02/2001


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0)
Java HotSpot(TM) Client VM (build 1.3.0, mixed mode)


The lookup table used by Class's getConstructor(Class[] paramClasses) doesn't
find the constructor if any of the paramClasses are concrete implementations of
interfaces in the constructor.

Here's two classes that demonstrate the problem. Get the output by running "java
Invoker"

--------------

import java.io.Serializable;

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

public class Invoker
{
    public Invoker()
    {}

    private static void printArray(Object[] array)
    {
        System.out.print("[ ");
        for(int i=0;i<array.length;i++)
            {
                System.out.print(array[i].toString()+" ");
            }
        System.out.println(" ]");
    }

    public static Victem works()
        throws
NoSuchMethodException,InstantiationException,IllegalAccessException,InvocationTargetException
    {
        System.out.println("Inside works");

        Class concrete = Victem.class;
        Serializable id = new Long(0);
        Object[] params = new Object[1];
        
        params[params.length-1] = id;
        System.out.println("params are "+params);
        printArray(params);

        //get the classes of the parameters
        Class[] paramClasses = new Class[params.length];
        for(int i=0;i<params.length;i++)
            {
                paramClasses[i] = params[i].getClass();
            }

        //I added this line to make the method work
        paramClasses[paramClasses.length-1]=Serializable.class;

        System.out.println("paramClasses are "+paramClasses);
        printArray(paramClasses);

        //Get the right constructor
        Constructor constructor = concrete.getConstructor(paramClasses);
        
        //create a new instance of the concrete class
        Victem result = (Victem)constructor.newInstance(params);
        
        System.out.println("-------------");
        //return the concrete class
        return result;
    }

    public static Victem broken()
        throws
NoSuchMethodException,InstantiationException,IllegalAccessException,InvocationTargetException
    {
        System.out.println("Inside broken");

        Class concrete = Victem.class;
        Serializable id = new Long(0);
        Object[] params = new Object[1];
        
        params[params.length-1] = id;
        System.out.println("params are "+params);
        printArray(params);

        //get the classes of the parameters
        Class[] paramClasses = new Class[params.length];
        for(int i=0;i<params.length;i++)
            {
                paramClasses[i] = params[i].getClass();
            }

        System.out.println("paramClasses are "+paramClasses);
        printArray(paramClasses);

        //Get the right constructor
        Constructor constructor = concrete.getConstructor(paramClasses);
        
        //create a new instance of the concrete class
        Victem result = (Victem)constructor.newInstance(params);
        
        //return the concrete class
        return result;
    }

    public static void main(String args[])
    {
        try
            {
                works();
                broken();
            }
        catch(Exception ex)
            {
                ex.printStackTrace();
            }
    }
}
--------------------------

import java.io.Serializable;

public class Victem
{
    private Serializable id;

    public Victem(Serializable identifier)
    {
        id = identifier;
    }
}
------------------------
System.out gets
Inside works
params are [Ljava.lang.Object;@310d42
[ 0  ]
paramClasses are [Ljava.lang.Class;@5d87b2
[ interface java.io.Serializable  ]
-------------
Inside broken
params are [Ljava.lang.Object;@20c10f
[ 0  ]
paramClasses are [Ljava.lang.Class;@62eec8
[ class java.lang.Long  ]
java.lang.NoSuchMethodException
        at java.lang.Class.getConstructor0(Native Method)
        at java.lang.Class.getConstructor(Class.java:927)
        at Invoker.broken(Invoker.java:82)
        at Invoker.main(Invoker.java:96)

-----------------------------------------

I realize that the bug involves an ambiguous case: if a paramClass implements
two different interfaces and the class to be constructed has two constructors,
each of which takes one interface as a parameter, then the getConstructor()
method won't be able to figure out which constructor to choose. However, this
problem is not unique to the getConstructor() method. The javac compiler won't
compile a class if it detects this problem. You could change the
getConstructor() (and the getDeclaredMethod() method) to throw either a runtime
exception or a subclass of NoSuchMethodException. An
AmbiguousMethodMatchException could include a list of possible matching methods.

Try and compile this code to see what javac does:
------------------

import java.io.Serializable;

public class Ambiguous
{
    private Serializable id;

    public Ambiguous(Serializable identifier)
    {
        id = identifier;
    }

    public Ambiguous(Comparable compit)
    {

    }

    public static void main(String args[])
    {
        try
            {
                new Ambiguous(new Long(2));
            }
        catch(Exception ex)
            {
                ex.printStackTrace();
            }
    }
}
(Review ID: 114421) 
======================================================================

Comments
WORK AROUND Name: boT120536 Date: 01/02/2001 There's not really a good generic workaround, short of reimplementing the java VM's own method lookup table. I'm trying to do some very cool, very general programming using JavaBeans (See http://lyophilizer.sourceforge.net for the latest), but will have to hardwire a lot of crud, or repeat some tedious coding you've already done, until you fix this bug. I'll have to type arround it, perhaps with code generation (yuck) until you fix it. Thanks, Dave ======================================================================
11-06-2004

EVALUATION You're essentially asking the reflection interface to do overload resolution just like the compiler, rather than requiring an exact match. I don't think we can change the semantics of the existing reflection interface, but adding a separate interface that does that overload resoltution would certainly be reasonable. I've marked this as an RFE. By the way, the documentation for the existing interface is not clear; it says that it finds the constructor whose formal parameters "match", but doesn't define what it means by "match". There is currently nothing in the VM that does this kind of computation. Adding something in the libraries to do this is prone to errors, as it would duplicate the algorithm in the compiler. neal.gafter@Eng 2001-08-02 See also 4287725. neal.gafter@Eng 2001-08-02
02-08-2001