Вот довольно простой способ сделать это. getConstructorForArgs
-метод проходит по всем конструкторам в данном классе и проверяет, соответствуют ли параметры конструктора указанным параметрам (обратите внимание, что заданные параметры должны быть в том же порядке, что и в конструкторе). Реализации интерфейсов и подклассов также работают, потому что "совместимость" проверяется путем вызова isAssignableFrom
для аргумента конструктора (это заданный тип параметра, назначаемый типу параметра в конструкторе).
public class ReflectionTest
{
public Constructor<?> getConstructorForArgs(Class<?> klass, Class[] args)
{
//Get all the constructors from given class
Constructor<?>[] constructors = klass.getConstructors();
for(Constructor<?> constructor : constructors)
{
//Walk through all the constructors, matching parameter amount and parameter types with given types (args)
Class<?>[] types = constructor.getParameterTypes();
if(types.length == args.length)
{
boolean argumentsMatch = true;
for(int i = 0; i < args.length; i++)
{
//Note that the types in args must be in same order as in the constructor if the checking is done this way
if(!types[i].isAssignableFrom(args[i]))
{
argumentsMatch = false;
break;
}
}
if(argumentsMatch)
{
//We found a matching constructor, return it
return constructor;
}
}
}
//No matching constructor
return null;
}
@Test
public void testGetConstructorForArgs()
{
//There's no constructor in HashSet that takes a String as a parameter
Assert.assertNull( getConstructorForArgs(HashSet.class, new Class[]{String.class}) );
//There is a parameterless constructor in HashSet
Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{}) );
//There is a constructor in HashSet that takes int as parameter
Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{int.class}) );
//There is a constructor in HashSet that takes a Collection as it's parameter, test with Collection-interface
Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{Collection.class}) );
//There is a constructor in HashSet that takes a Collection as it's parameter, and HashSet itself is a Collection-implementation
Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{HashSet.class}) );
//There's no constructor in HashSet that takes an Object as a parameter
Assert.assertNull( getConstructorForArgs(HashSet.class, new Class[]{Object.class}) );
//There is a constructor in HashSet that takes an int as first parameter and float as second
Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{int.class, float.class}) );
//There's no constructor in HashSet that takes an float as first parameter and int as second
Assert.assertNull( getConstructorForArgs(HashSet.class, new Class[]{float.class, int.class}) );
}
}
Редактировать : обратите внимание, что это решение НЕ идеально для всех случаев: если есть два конструктора, которые имеют параметр, который можно назначить из данного типа параметра, будет выбран первый, даже если второй был лучше подходит. Например, если SomeClass
будет иметь конструктор, который принимает HashSet
(A Collection
-имплементация) в качестве параметра, и конструктор, принимающий Collection
в качестве параметра, метод может вернуть любой из них при поиске конструктор, принимающий HashSet
в качестве параметра, в зависимости от того, что было первым при переборе классов. Если это также необходимо для таких случаев, вам необходимо сначала собрать всех возможных кандидатов, которые соответствуют isAssignableFrom
, а затем провести более глубокий анализ кандидатов, чтобы выбрать наиболее подходящего.