Class.getDeclaredMethods() is returning inherited methods

Roger Riggs Roger.Riggs at oracle.com
Thu Jan 3 14:22:28 UTC 2019


Hi,

With a link to the explanation added to the issue, I think it can be 
closed as not-an-issue.

Roger


On 01/03/2019 07:19 AM, Steve Groeger wrote:
> Hi Peter,
>
> Thanks again for your explanation.
> It was the public vs. non-public  part that I had missed and I was getting
> confused thinking it was related to abstract vs. non-abstract classes.
> The generation of the synthetic methods and why the methods are shown via
> the getDeclaredMethods() makes a lot more sense now.
>
> Thanks
> Steve Groeger
> IBM Runtime Technologies
> Hursley, Winchester
> Tel: (44) 1962 816911  Mobex: 279990  Mobile: 07718 517 129
> Fax (44) 1962 816800
> Lotus Notes: Steve Groeger/UK/IBM
> Internet: groeges at uk.ibm.com
>
> Unless stated otherwise above:
> IBM United Kingdom Limited - Registered in England and Wales with number
> 741598.
> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU
>
>
>
> From:   Peter Levart <peter.levart at gmail.com>
> To:     Steve Groeger <GROEGES at uk.ibm.com>
> Cc:     OpenJDK Core Libs Developers <core-libs-dev at openjdk.java.net>
> Date:   03/01/2019 11:33
> Subject:        Re: Class.getDeclaredMethods() is returning inherited
> methods
>
>
>
> Hi Steve,
>
> The difference you observe between variants of class A is not about the
> "abstract" vs. non-"abstract" but about "public" vs. non-"public"...
>
> Your variants of class A are:
>
> public class A {
>       public void foo() {}
> }
>
> vs.
>
> abstract class A {
>       public void foo() {}
> }
>
>
> The 1st is public but the 2nd is package-private. Try with the following
> variant:
>
> class A {
>       public void foo() {}
> }
>
> ... and you'll see the same behavior as with abstract package-private
> class. Try also with the following variant:
>
> public abstract class A {
>       public void foo() {}
> }
>
> ...and you'll see the same behavior as with public concrete class.
>
>
> The reason why javac does not generate synthetic method when extending
> public class is obvious. It is not needed since A.m() can be invoked
> from anywhere if A is public.
>
> Regards, Peter
>
> On 1/3/19 10:33 AM, Steve Groeger wrote:
>> Hi Peter,
>>
>> Thank you very much for your detailed and very informative explanation.
> I
>> have one more question for clarification.
>> If I have the following code:
>>
>> abstract class A {
>>       public void foo() {}
>> }
>>
>>
>> import java.lang.reflect.*;
>>
>> public class B extends A {
>>       public static void main(String[] args) throws Exception {
>>           System.out.println("----- getDeclaredMethods -----");
>>           Method[] methods = B.class.getDeclaredMethods();
>>           for( int i = 0 ; i < methods.length ; i++ ){
>>               System.out.println(methods[i]);
>>           }
>>       }
>> }
>>
>> If I compile and run this class "java --classpath . B" I get I get the
>> following output:
>>
>> ----- getDeclaredMethods -----
>> public static void B.main(java.lang.String[]) throws java.lang.Exception
>> public void B.foo()
>>
>> which seems to fit with your explanation  (noting that A is an abstract
>> class). However, if I make A a public class
>>
>> public class A {
>>       public void foo() {}
>> }
>>
>>
>> the output is different and doesnt contain the foo() method.
>>
>> ----- getDeclaredMethods -----
>> public static void B.main(java.lang.String[]) throws java.lang.Exception
>>
>> Is this as expected?
>>
>> Should there be a difference between the output from running the
>> getDelaredMethods() on
>> 1) a class extending an abstract class with a public method (non
> abstract)
>> and
>> 2) a class extending a public class with a public method?
>>
>>
>> Thanks
>> Steve Groeger
>> IBM Runtime Technologies
>> Hursley, Winchester
>> Tel: (44) 1962 816911  Mobex: 279990  Mobile: 07718 517 129
>> Fax (44) 1962 816800
>> Lotus Notes: Steve Groeger/UK/IBM
>> Internet: groeges at uk.ibm.com
>>
>> Unless stated otherwise above:
>> IBM United Kingdom Limited - Registered in England and Wales with number
>> 741598.
>> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
> 3AU
>>
>>
>> From:   Peter Levart <peter.levart at gmail.com>
>> To:     Steve Groeger <GROEGES at uk.ibm.com>, OpenJDK Core Libs Developers
>> <core-libs-dev at openjdk.java.net>
>> Date:   02/01/2019 17:45
>> Subject:        Re: Class.getDeclaredMethods() is returning inherited
>> methods
>>
>>
>>
>> Hi Steve,
>>
>> What you have observed is an artifact of how Java the language is
>> compiled into bytecode contained in .class files and the fact that Java
>> reflection API returns information about the compiled .class files. The
>> quoted sample code:
>>
>> package x.y;
>>
>> class A {
>>      public void m() {
>>      }
>> }
>>
>> public class B {
>> }
>>
>> ...is one example where the compiler must create a synthetic method in
>> class B which overrides public method in class A and delegates to it.
>> Java access checks allow invoking A.m() only from package x.y since A is
>> a package private class. OTOH m() is a public method and as such is
>> inherited by B. so m() can be called on an instance of type B from
>> anywhere because B is a public class. To accommodate that, javac
>> synthesizes public method B.m() which delegates to A.m() as thought you
>> would have written the following code:
>>
>> package x.y;
>>
>> class A {
>>      public void m() {
>>      }
>> }
>>
>> public class B {
>>      public void m() {
>>        super.m();
>>      }
>> }
>>
>>
>> ... B.class.getDeclaredMethods() therefore returns this synthetic method
>> B.m() and not the inherited method A.m(). You can verify that by
>> invoking .getDeclaringClass() on it, which should give you B.class and
>> not A.class.
>>
>> Strictly speaking such synthetic method on B is not really needed by JVM
>> to invoke A.m() from anywhere via the instance of B. Invocation of m()
>> given an instance of B could be compiled by javac in exactly the same
>> way even if there was no synthetic method B.m(). Javac could pretend
>> there is a method B.m() and emit invoke instruction referencing the
>> imaginative method. At runtime JVM would dispatch the virtual call to
>> inherited A.m and allow such call from anywhere since it references a
>> public method in a public class (although imaginative).
>>
>> The only reason javac generates synthetic method B.m() is to actually
>> allow A.m() to be called via reflection API. Reflection API does not
>> allow invoking A.m() directly from anywhere, but it allows invoking the
>> synthetic B.m() which then delegates to A.m().
>>
>> If we wanted to get rid of this synthetic method, reflection would have
>> to be fixed 1st to accommodate calling public methods inherited from
>> non-public classes if they are called via an instance of a public
>> subclass (or a subclass of it). The reason this has not been done yet is
>> probably that it is more tricky that it seems at first. Imagine the
>> following situation:
>>
>> package p1;
>>
>> class A {
>>        public void m() {
>>        }
>> }
>>
>> public class B extends A {
>> }
>>
>>
>> package p2;
>>
>> class C extends B {
>> }
>>
>> public class Builder {
>>        public static B build() {
>>            return new C();
>>        }
>> }
>>
>>
>> package p3;
>>
>> public class App {
>>        public static void main(String[] args) {
>>            B b = p2.Builder.build();
>>            b.m(); // << HERE
>>        }
>> }
>>
>>
>> ...javac would have an easy job here. It knows the static (compile-time)
>> type of variable b (which is a public class B) so it can directly emit
>> invoke instruction for imaginative method B.m() here.
>>
>> Reflection API OTOH has a more difficult job in the following code:
>>
>> package p3;
>>
>> public class App {
>>        public static void main(String[] args) {
>>            B b = p2.Builder.build();
>>            Method m = B.class.getMethod("m"); // m.getDeclatingClass() ==
>> A.class
>>            m.invoke(b); // << HERE
>>        }
>> }
>>
>> ...reflection has at its disposal:
>>
>> - a Method object 'm' representing method A.m() which is located in a
>> non-public class p1.A
>> - a target instance 'b' which is of type p2.C which is also a non-public
>> class
>> - the invocation is being performed from package p3
>>
>> In order for reflection to allow such invocation it would have to assume
>> that such invocation is performed via some imaginative method X.m() such
>> that:
>> - X is a public class and is a subclass of A
>> - C is a subclass of X
>>
>> Such decision is inherently non-local meaning that reflection would have
>> to search class hierarchy to allow it. It is possible though, but
>> probably to complicated for such use case.
>>
>> What do others think?
>>
>> Regards, Peter
>>
>>
>> On 1/2/19 5:33 PM, Steve Groeger wrote:
>>> I am looking into an issue where the Class.getDeclaredMethods() is
>>> returning inherited methods,
>>> where the Java Doc here:-
>>>
> https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/lang/Class.html#getDeclaredMethods()
>
>>> states:-
>>>
>>> "Returns an array containing Method objects reflecting all the declared
>>> methods of the class or interface represented by this Class object,
>>> including public, protected, default (package) access, and private
>>> methods, but excluding inherited methods".
>>>
>>> with the last part of the statement being the relevant part, "but
>>> excluding inherited methods"
>>>
>>> This was raised as an issue a long time ago:
>>>
> https://bugs.openjdk.java.net/browse/JDK-6815786
>
>> but has not been fixed
>>> and is still an issue in JDK11.
>>>
>>> Before I go looking into why this is occurring and producing a fix, is
>>> this still seen as an issue and does it need to be fixed / should it be
>>> fixed.
>>> I dont want to do lots of investigation and produce a fix just to be
>> told
>>> we cant contribute this as it will break too many people that might
> have
>>> been using this feature !!!!
>>>
>>> Thanks
>>> Steve Groeger
>>> IBM Runtime Technologies
>>> Hursley, Winchester
>>> Tel: (44) 1962 816911  Mobex: 279990  Mobile: 07718 517 129
>>> Fax (44) 1962 816800
>>> Lotus Notes: Steve Groeger/UK/IBM
>>> Internet: groeges at uk.ibm.com
>>>
>>> Unless stated otherwise above:
>>> IBM United Kingdom Limited - Registered in England and Wales with
> number
>>> 741598.
>>> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
>> 3AU
>>> Unless stated otherwise above:
>>> IBM United Kingdom Limited - Registered in England and Wales with
> number
>>> 741598.
>>> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
>> 3AU
>>
>>
>>
>>
>>
>> Unless stated otherwise above:
>> IBM United Kingdom Limited - Registered in England and Wales with number
>> 741598.
>> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6
> 3AU
>
>
>
>
> Unless stated otherwise above:
> IBM United Kingdom Limited - Registered in England and Wales with number
> 741598.
> Registered office: PO Box 41, North Harbour, Portsmouth, Hampshire PO6 3AU



More information about the core-libs-dev mailing list