Class.getDeclaredMethods() is returning inherited methods
David Holmes
david.holmes at oracle.com
Thu Jan 3 22:58:34 UTC 2019
Hi Roger,
On 4/01/2019 12:22 am, Roger Riggs wrote:
> Hi,
>
> With a link to the explanation added to the issue, I think it can be
> closed as not-an-issue.
Do you think the Class.getDeclared* method specs should be updated to
reflect (pardon the pun) that synthetic methods/constructors will be
included?
Cheers,
David
> 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