Comment made by: mruza
I've added tests and updated the patch according to the instructions.
Here is some reasoning behind it. Below is an excerpt from the src/jvm/clojure/lang/Compiler.java file:
1462: if(target.hasJavaClass() && target.getJavaClass() != null)
1463: {
1464: List methods = Reflector.getMethods(target.getJavaClass(), args.count(), methodName, false);
1465: if(methods.isEmpty())
1466: {
1467: method = null;
1468: if(RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
1469: {
1470: RT.errPrintWriter()
1471: .format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (no such method).\n",
1472: SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName());
1473: }
1474: }
1475: else
1476: {
1477: int methodidx = 0;
1478: if(methods.size() > 1)
1479: {
1480: ArrayList<Class[]> params = new ArrayList();
1481: ArrayList<Class> rets = new ArrayList();
1482: for(int i = 0; i < methods.size(); i++)
1483: {
1484: java.lang.reflect.Method m = (java.lang.reflect.Method) methods.get(i);
1485: params.add(m.getParameterTypes());
1486: rets.add(m.getReturnType());
1487: }
1488: methodidx = getMatchingParams(methodName, params, args, rets);
1489: }
1490: java.lang.reflect.Method m =
1491: (java.lang.reflect.Method) (methodidx >= 0 ? methods.get(methodidx) : null);
1492: if(m != null && !Modifier.isPublic(m.getDeclaringClass().getModifiers()))
1493: {
1494: //public method of non-public class, try to find a public descendant
1495: if((type=Reflector.getDeepestPublicDescendant(m.getDeclaringClass(), target.getJavaClass())) == null)
1496: //if descendant not found, try to find an ancestor
1497: m = Reflector.getAsMethodOfPublicBase(m.getDeclaringClass(), m);
1498: }
1499: method = m;
1500: if(method == null && RT.booleanCast(RT.WARN_ON_REFLECTION.deref()))
1501: {
1502: RT.errPrintWriter()
1503: .format("Reflection warning, %s:%d:%d - call to method %s on %s can't be resolved (argument types: %s).\n",
1504: SOURCE_PATH.deref(), line, column, methodName, target.getJavaClass().getName(), getTypeStringForArgs(args));
1505: }
1506: }
1507: }
- the condition on line 1462 ensures that the type/class of the target is known
- the {{clojure.lang.Reflector.getMethods()}} method called on line 1464 returns a list of all public methods of the given name defined for the target type
- then the best method to call is selected on lines 1477-1491
- if the declaring class of the selected method is not public then an attempt is made to find a public class which is both superclass of the target type and a subclass of the class declaring the selected method - this is implemented in the {{clojure.lang.Reflector.getDeepestPublicDescendant()}} method
- if such a class is found than it is used instead of the method's declaring class when emitting the byte code for the method call
- if no such class is found then an attempt is made to find a compatible method in the public ancestors of the class declaring the selected method
Note that the change may result in a different method being called than prior to the change as demonstrated by the {{selecting-method-on-nonpublic-interface}} test. This is IMO an acceptable change as it:
results in better matching (with respect to the argument types) method to be called
makes the method selection in clojure behave in a more similar way to that in java