Class.getDeclaredMethods() is returning inherited methods

Steve Groeger GROEGES at uk.ibm.com
Thu Jan 3 12:19:40 UTC 2019


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