[External] : Re: Can not call a private method, no idea why ?

forax at univ-mlv.fr forax at univ-mlv.fr
Thu Jan 21 14:29:01 UTC 2021


> De: "Maurizio Cimadamore" <maurizio.cimadamore at oracle.com>
> À: "Remi Forax" <forax at univ-mlv.fr>
> Cc: "Anna Kozlova" <anna.kozlova at jetbrains.com>, "compiler-dev"
> <compiler-dev at openjdk.java.net>
> Envoyé: Jeudi 21 Janvier 2021 14:03:50
> Objet: Re: [External] : Re: Can not call a private method, no idea why ?

>>> I don't believe that's the right analogy; this is:

>>> ```
>>> import java.util.function.Function;

>>> class Test {
>>> private void greet() { System.out.println("hello"); }

>>> <X extends Test> void test(X x) {
>>> x.greet();
>>> }
>>> }
>>> ```

>>> Which also fails, no wildcards around this time.

>>> When you access a member on a `? extends T` you get back a captured variable,
>>> which is a fresh type variable with some bounds. As per JLS 4.4 the members of
>>> a type variable X extends A & I1 ... In are the members of the intersection
>>> type (A, I1 .. In). In the definition of intersection type in JLS 4.9 you will
>>> see that only public members are allowed.

>>> This decision comes from the fact that type-variables have a "for all" spirit to
>>> them - when you write something like x.e, and x is of type X (with X a type
>>> variable) the intent of the language is for x.e to either be valid _for all_
>>> instantiations of X, or to be rejected. Without this rule, it would be very
>>> easy to write a generic class which can be instantiated in ways that make it
>>> break some (runtime) assumptions.
>> Given that private method are not dynamically linked, i don't see how this is
>> issue at runtime.

>> Back to the first example, if you introduce a 'var' in the middle, it will
>> compile

>> class Test {
>> private void greet() { System.out.println("hello"); }

>> void test(Function<? super Object, ? extends Test> f) {
>> f.apply(null).greet(); // nope
>> var t = f.apply(null);
>> t.greet(); // yes !
>> }
>> }

>> so i don't think itcan break some runtime assumptions.

> I think your example is subtly misleading - you are using `var` and `var` has
> special normalization for avoiding captured variables - which means that
> accessing apply(null).greet() and t.greet() have completely different static
> types.
yes, my example just shows that you will not get an exception at runtime. 
Not that both codes are equivalent in term of types, there are only equivalent at runtime. 

> To back off form this specific example, think of this:

> public class Box<X extends Foo> {
> X x;

> Box(X x) { this.x = x; }

> void m() {
> x. ???
> }
> }

> What are the valid member access for "x" ?

> Say that Box and Foo are in the same package - can I access all members of "Foo"
> ? Note that this Box class can be used from a completely different package, so
> there will be runtime issues if we bake in assumptions on which members can be
> accessed from the type variable.
This is a different question, in the first code, the greet() was private. 
To answer to your question all members of Foo that are visibles from Box. 

> In other words - if Box<T> is a well-formed type, I want a guarantee that no bad
> stuff can happen at runtime (yes, there can be unchecked casts and all that -
> but let's assume that all the code compiled w/o warnings). And if you allow
> access to anything other than public members you have no such guarantee.

> I sympathize with your argument: at the end of the day, when you have X <: Foo,
> you can always widen (e.g. assign) X to Foo (w/o warnings) and take it from
> there, and all restrictions are gone - so maybe this is excessive hand-holding
> on the language-side - but I was trying to explain the rationale behind the
> rules - it is pretty common when dealing with parametric polymorphism to think
> of membership of a type-variable X in a "for all" semantics - e.g. the
> intersection of members that are available in all instantiations of X; so, for
> the language (and the compiler) X <: Foo and Foo are two very different types,
> with very different membership rules.
as i said, you can use the same "for all" argument for subtyping too, like Ruby does, where you can only access to the private members from this. 

> Maurizio
Rémi 

>>> Maurizio
>> Rémi

>>>>> Maurizio
>>>> Rémi

>>>>> On 21/01/2021 10:53, Anna Kozlova wrote:

>>>>>> Hi Remi,

>>>>>> type of `map.apply(null)` is capture of `? extends IOBug` and thus it has no
>>>>>> access to private methods of IOBug. Do I miss something?

>>>>>> Thanks,
>>>>>> Anna

>>>>>> On Thu, Jan 21, 2021 at 9:34 AM Remi Forax < [ mailto:forax at univ-mlv.fr |
>>>>>> forax at univ-mlv.fr ] > wrote:

>>>>>>> The following code doesn't compile.
>>>>>>> No idea why ?

>>>>>>> public interface IOBug {
>>>>>>> private IOBug foo() {
>>>>>>> return this;
>>>>>>> }

>>>>>>> default IOBug flatMap(Function<? super Object, ? extends IOBug> map) {
>>>>>>> return map.apply(null).foo();
>>>>>>> ^------
>>>>>>> }
>>>>>>> }

>>>>>>> It works if foo is declared as a default method instead of a private method.

>>>>>>> regards,
>>>>>>> Rémi
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.java.net/pipermail/compiler-dev/attachments/20210121/6bd731b5/attachment.htm>


More information about the compiler-dev mailing list