Home > Software engineering >  Why is Java function Supplier allowing to execute private methods from another class?
Why is Java function Supplier allowing to execute private methods from another class?

Time:08-24

import java.util.*;
import java.util.function.*;

public class MethodReference {

    public static void main(String args[]) {
        MethodHolder mh = new MethodHolder();
        System.out.println(mh.methodMap.get(1).get());
        System.out.println(mh.methodMap.get(2).get());
        System.out.println(mh.methodMap.get(3).get());
        System.out.println(mh.methodMap.get(4).get());
    }
}

class MethodHolder {
    public Map<Integer, Supplier<String>> methodMap = new HashMap<>();
    private int count = 1;

    public MethodHolder() {
        methodMap.put(1, this::method1);
        methodMap.put(2, this::method2);
        methodMap.put(3, this::method3);
        methodMap.put(4, this::method4);
    }

    public String method1() {
        return "public method1 - "   (  count);
    }

    protected String method2() {
            return "protected method2 - "   (  count);
    }

    String method3() {
                return "default method3 - "   (  count);
    }

    private String method4() {
                return "private method4 - "   (  count);
    }
}

Doesn't this break the method access specifier contract?

We can access private using Reflection but is Supplier too using reflection? What is the rationale behind this?

CodePudding user response:

This does not "break the method access specifier contract" because access modifiers control where you can access things. The access to method2 and method4 occurs inside MethodHolder - the same class where they are declared.

// this happens in the MethodHolder class:
methodMap.put(1, this::method1);
methodMap.put(2, this::method2);
methodMap.put(3, this::method3);
methodMap.put(4, this::method4);

So nothing is wrong here.

It does not matter where you call get. After all, when you call get, you are accessing the method get, which is a public method declared in Supplier, so you are allowed to access it from anywhere.

The fact that calling get actually causes the method method4 in your MethodHolder to be executed, is none of the business of access modifiers. That is an implementation detail.

And no, method references are not implemented with reflection. They are implemented with an invokedynamic instruction - a class implementing the Supplier interface is dynamically created at runtime and instantiated, when the method reference expression is evaluated.


Basically, claiming that this "breaks the method access specifier contract" is like saying that, since calling publicMethod (see below) from outside MyClass causes privateMethod to be executed, allowing public methods to call private methods also "breaks the method access specifier contract"

public class Main {
    public static void main(String[] args) {
        new MyClass().publicMethod(); // I can cause privateMethod to be executed here!
        // Does this "break access specifier contract"? No!
    }
}

public class MyClass {
    public void publicMethod() { privateMethod(); }
    private void privateMethod() {}
}

In the same way that calling privateMethod is just an implementation detail of publicMethod in the above example, Your method4 is just an implementation of get, albeit in a less obvious way, because it is a method reference.

CodePudding user response:

The code:

    methodMap.put(4, this::method4);

Is equivalent to:

    methodMap.put(4, new Supplier<String>() {
        @Override
        public String get() {
            return MethodHolder.this.method4();
        }
    });

This code is in the MethodHolder class, so it can use any method of this class, including private ones.

CodePudding user response:

Doesn't this break the method access specifier contract?

No, it does not because you are calling method by using methodMap.

methodMap is public, however, it can call all methods and properties of class with any access modifier where it is placed.

It is not usual way to use methods of class, but you can.

Usually, mapping is used when Factory pattern can be applied. Let me show an example via C#:

public enum SubscriptionType 
{
    WebSocket, Grpc
}


public class PublisherFactory
{
    private Dictionary<SubscriptionType, Publisher> _pubisherByType = 
        new Dictionary<SubscriptionType, Publisher>()
    {
        { SubscriptionType.WebSocket, new  WebSocketPublisher () },
        { SubscriptionType.Grpc, new GrpcPublisher () },
    };

    public Publisher GetInstanceByType(SubscriptionType courtType) => 
        _pubisherByType[courtType];
}
  • Related