JDK-8144122 : Class.getDeclaredMethods() return value includes shadowed methods
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang:reflect
  • Affected Version: 6,7,8,9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: x86_64
  • Submitted: 2015-10-01
  • Updated: 2018-05-31
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbd_minorUnresolved
Description
FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) 64-Bit Server VM (build 25.60-b23, mixed mode)

A DESCRIPTION OF THE PROBLEM :
According to the JavaDoc of Class.getDeclaredMethods(), which cites: 
"Returns an array of {@code Method} objects reflecting all the methods declared by the class or interface represented by this {@code Class} object. This includes public, protected, default (package) access, and private methods, but excludes inherited methods. (...)", 
only methods declared on the class should be returned. However, the returned value also includes shadowed methods.

This causes problems when the method is used in order to determine a return type of a particular method, which overrides one from its parent.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a file named Main.java, containing attached source code.
2. Compile it from command line:
"javac Main.java"
3. Run it with enable asserts option: 
"java -ea Main"

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Number of declared methods is 2.
ACTUAL -
Number of declared methods is 4.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
public class Main {

    public static void main(String[] args) {
        int declaredMethods = IntegerStringAdapter.class.getDeclaredMethods().length;
        System.out.println(String.format("Number of declared methods is %s.", declaredMethods));
    }

    static class XmlAdapter<ValueType, BoundType> {
        public BoundType unmarshal(ValueType v) {return null;}
        public ValueType marshal(BoundType v) {return null;}
    }

    static class IntegerStringAdapter extends XmlAdapter<Integer, String> {
        public String unmarshal(Integer i) {
            return i.toString();
        }
        public Integer marshal(String s) {
            return Integer.getInteger(s);
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Post-process the returned array of Methods and manually shadow the inherited shadowed methods, effectively excluding them from the search space.

The following code examples illustrate that:
--------------------------------
1. Ambiguous result - BAD. Calling this code might return the return type of the shadowed method.

public static Class<?> getBoundTypeFromAdapterClass(Class<? extends XmlAdapter> adapterClass) {
        for (Method method : adapterClass.getDeclaredMethods()) {
            if (method.getName().equals("unmarshal")) {
                return method.getReturnType();
            }
        }
        return null;
    }

--------------------------------
2. Deterministic result - GOOD. Calling this code will return the return type of the shadowing method.

public static Class<?> getBoundTypeFromAdapterClass(Class<? extends XmlAdapter> adapterClass) {

        for (Method method : adapterClass.getDeclaredMethods()) {
            if (method.getName().equals("unmarshal")) {
                Class<?> methodReturnType = method.getReturnType();
                if (methodReturnType != Object.class) {
                    return methodReturnType;
                }
            }
        }
        return Object.class;
    }


Comments
IntegerStringAdapter declares 4 methods, the two visible in the source, + 2 bridge methods: Verify with javap -p -v Main.IntegerStringAdapter Compiled from "Main.java" class Main$IntegerStringAdapter extends Main$XmlAdapter<java.lang.Integer, java.lang.String> { Main$IntegerStringAdapter(); public java.lang.String unmarshal(java.lang.Integer); public java.lang.Integer marshal(java.lang.String); public java.lang.Object marshal(java.lang.Object); public java.lang.Object unmarshal(java.lang.Object); }
07-12-2015

This is an issue and it exist from Java 6 and all the builds including 9 ea Actually it should not return the methods declared by the base class. EXPECTED - Number of declared methods is 2. ACTUAL - Number of declared methods is 4. This issue observed when method is used in order to determine a return type of a particular method, which overrides one from its parent
26-11-2015

This is not a javac issue, it's a core reflection one.
26-11-2015