Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-6491615

(reflect) isAssignableFrom doesn't test if one class is assignable from another class

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: P4 P4
    • None
    • 5.0
    • core-libs

      FULL PRODUCT VERSION :


      ADDITIONAL OS VERSION INFORMATION :
      Windows XP

      A DESCRIPTION OF THE PROBLEM :
      isAssignableFrom checks from assignment of class but somewhat ignore primatives. Is there a method which checks for assignment of classes AND primatives.

      What is the issue?
      The problem I have is that the method doesn't do what it suggests it
      should do and there is no other method which does.
      When the method was first written perhaps it was correct behaviour,
      however with auto-boxing and reflection it is more accurate to say
      Double and double can be assigned to each other.

      Why do I need it?
      I need to be able to test whether by reflection an object can be
      assigned to a field or can be passed as an argument to a method. To do
      this I need a test which will say a 'Double' can be used to set a field
      of type 'double'. (In fact you cannot pass a double as an object in any
      case.)

      How can this be addressed?
      Currently the only way to address this is to write method which handle
      primatives in a way which is consistent with the way Object types are
      handled.

      The following method caches results to avoid native calls and repeatedly
      throwing ClassNotFoundException. However these could be written without
      such caching.

          private static final Map<Class, Class> PRIMATIVE_MATCH = new
      LinkedHashMap<Class, Class>() {
              {
                  put(long.class, Long.class);
                  put(int.class, Integer.class);
                  put(double.class, Double.class);
                  put(short.class, Short.class);
                  put(byte.class, Byte.class);
                  put(float.class, Float.class);
                  put(boolean.class, Boolean.class);
                  put(char.class, Character.class);
              }
          };

          private static final Map<String, Class> CLASS_LOOKUP = new
      ConcurrentHashMap<String, Class>() {
              {
                  for (Class clazz : PRIMATIVE_MATCH.keySet())
                      put(clazz.getName(), clazz);
              }
          };

          private static final Map<Pair<Class,Class>, Boolean>
      IS_ASSIGNABLE_FROM = new ConcurrentHashMap<Pair<Class, Class>,
      Boolean>();

          public static boolean isAssignableFrom(@NotNull Class class1,
      @NotNull Class class2) {
              if (class1 == class2 || class1 == Object.class) return true;
              Class pclass1 = PRIMATIVE_MATCH.get(class1);
              if (pclass1 != null)
                  return pclass1 == class2;
              Class pclass2 = PRIMATIVE_MATCH.get(class2);
              if (pclass2 != null)
                  return pclass1 == class2;
              Pair<Class,Class> classPair = new Pair<Class, Class>(class1,
      class2);
              Boolean ret = IS_ASSIGNABLE_FROM.get(classPair);
              if (ret == null)
                  IS_ASSIGNABLE_FROM.put(classPair, ret =
      class1.isAssignableFrom(class2));
              return ret;
          }

          @Nullable public static Class forName(@NotNull String name) {
              Class clazz = CLASS_LOOKUP.get(name);
              // ConcurrentHashMap doesn't support null values so void.class
      is used instead as no value can be of type void.
              if (clazz != null) return clazz == void.class ? null : clazz;
              Class clazz2;
              try {
                  clazz2 = Class.forName(name);
              } catch (ClassNotFoundException ignored) {
                  clazz2 = void.class;
              }
              CLASS_LOOKUP.put(name, clazz2);
              return clazz2;
          }

      REGRESSION. Last worked in version mustang

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See unit test below.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      That if one class can be assigned to another class isAssignableTo() should return true.
      ACTUAL -
      It only checks for Objects and exact matches for primatives.
      In the source code, these primatives/non-primatives are assigned to one another using regular '=' assignment and via reflection but isAssignableFrom returns false in every case.

      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import junit.framework.*;
      import java.lang.reflect.*;
      public class A extends TestCase {
          public void test_isAssignment() throws NoSuchFieldException, IllegalAccessException {
              PrimativeClass pc = new PrimativeClass();
              ObjectClass oc = new ObjectClass((byte) 0, (short) 0, 0, 0L, 0.0f, 0.0, '0');
              pc._byte = oc._byte;
              pc._short = oc._short;
              pc._integer = oc._integer;
              pc._long = oc._long;
              pc._float = oc._float;
              pc._double = oc._double;
              pc._char = oc._char;

              oc._byte = pc._byte;
              oc._short = pc._short;
              oc._integer = pc._integer;
              oc._long = pc._long;
              oc._float = pc._float;
              oc._double = pc._double;
              oc._char = pc._char;

              // copy using reflection.
              for(Field f : PrimativeClass.class.getDeclaredFields())
                  ObjectClass.class.getField(f.getName()).set(oc, f.get(pc));

              // copy using reflection.
              for(Field f : ObjectClass.class.getDeclaredFields())
                  PrimativeClass.class.getField(f.getName()).set(pc, f.get(oc));

              Class[] class1 = {byte.class, short.class, int.class, long.class, float.class, double.class, char.class};
              Class[] class2 = {Byte.class, Short.class, Integer.class, Long.class, Float.class, Double.class, Character.class};
              for (int i = 0; i < class1.length; i++) {
                  System.out.println(class1[i] + " isAssignableFrom( " + class2[i] + ") is " + class1[i].isAssignableFrom(class2[i]));
                  System.out.println(class2[i] + " isAssignableFrom( " + class1[i] + ") is " + class2[i].isAssignableFrom(class1[i]));
              }
          }

          public static class PrimativeClass {
              public byte _byte;
              public short _short;
              public int _integer;
              public long _long;
              public float _float;
              public double _double;
              public char _char;
          }

          public static class ObjectClass {
              public Byte _byte;
              public Short _short;
              public Integer _integer;
              public Long _long;
              public Float _float;
              public Double _double;
              public Character _char;

              public ObjectClass(Byte _byte, Short _short, Integer _integer, Long _long, Float _float, Double _double, Character _char) {
                  this._byte = _byte;
                  this._short = _short;
                  this._integer = _integer;
                  this._long = _long;
                  this._float = _float;
                  this._double = _double;
                  this._char = _char;
              }
          }
      }

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      Create own method for check if one class is a primative.

      Suggest adding a method which tests whether one class can be assigned to a field with another class.

            Unassigned Unassigned
            ndcosta Nelson Dcosta (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            1 Start watching this issue

              Created:
              Updated:
              Imported:
              Indexed: