Using Java Reflection, how do I get a class constructor that defines a derived class of the args constructor?

With Java reflection, you can get the constructor through getConstructor(klass, args).

However, when we pass as a argsderived class the class specified in the constructor signature, it fails. How to overcome this problem?

For instance,

HashSet.class.getConstructor(new Class[]{ HashSet.class });

fails. While

HashSet.class.getConstructor(new Class[]{ Collection.class });

succeeds.

I am looking for something that can be easily used in clojure. Therefore, I would prefer to have something out of the box and not add custom features.

Any idea how to solve this problem?

+5
source share
5 answers

Based on the answers from esaj and TJ Crowder :

, (1) (2) , . ( , , , , , , , nil.) (.. 'int/:int). , .

:

user> (find-best-constructors java.util.HashSet [:int :float])
(#<Constructor public java.util.HashSet(int,float)>)
user> (find-best-constructors java.util.HashSet [java.util.HashSet])
(#<Constructor public java.util.HashSet(java.util.Collection)>)
user> (find-best-constructors java.util.HashSet [Integer])
(#<Constructor public java.util.HashSet(int)>)

; , , IntegerLong .. convm if count-steps .

:

(defn find-best-constructors [klass args]
        (let [keym {:boolean Boolean/TYPE
                    :byte    Byte/TYPE
                    :double  Double/TYPE
                    :float   Float/TYPE
                    :int     Integer/TYPE
                    :long    Long/TYPE
                    :short   Short/TYPE}
              args (->> args
                        (map #(if (class? %) % (keyword %)))
                        (map #(keym % %)))
              prims (map keym [:boolean :byte :double :float :int :long :short])
              boxed [Boolean Byte Double Float Integer Long Short]
              convm (zipmap (concat prims boxed) (concat boxed prims))
              ctors (->> (.getConstructors klass)
                         (filter #(== (count args) (count (.getParameterTypes %))))
                         (filter #(every? (fn [[pt a]]
                                            (or (.isAssignableFrom pt a)
                                                (if-let [pt* (convm pt)]
                                                  (.isAssignableFrom pt* a))))
                                          (zipmap (.getParameterTypes %) args))))]
          (when (seq ctors)
            (let [count-steps (fn count-steps [pt a]
                                (loop [ks #{a} cnt 0]
                                  (if (or (ks pt) (ks (convm pt)))
                                    cnt
                                    (recur (set (mapcat parents ks)) (inc cnt)))))
                  steps (map (fn [ctor]
                               (map count-steps (.getParameterTypes ctor) args))
                             ctors)
                  m (zipmap steps ctors)
                  min-steps (->> steps
                                 (apply min-key (partial apply max))
                                 (apply max))]
              (->> m
                   (filter (comp #{min-steps} (partial apply max) key))
                   vals)))))
+4

HashSet HashSet(HashSet), , , , . , ( , , , ), .

+5

. 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 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 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 parameter, and HashSet itself is a Collection-implementation
        Assert.assertNotNull( getConstructorForArgs(HashSet.class, new Class[]{HashSet.class}) );

        //There 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 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 -implementation) , , Collection , , HashSet as, , . , , isAssignableFrom, , .

+5
source

Do not confuse polymorphic behavior here. Because you pass Collection as a concrete value, not a param type (new class [] {Collection}).

0
source

I think you can get the parent class and a list of all implemented interfaces -> so that you can check the Hashset constructor first. If nothing is found, you can do it recursively for all parent classes and interfaces until you find the one you need.

0
source

All Articles