5

When you define an abstract method of a class as public or global, an attempt to change the access modifier to protected or private when overriding the method produces a compile time error:

Cannot reduce the visibility of method: ...

Similarly for interface methods,

Overriding implementations of global or public interface methods must be global or public: ...

But why is it that Apex allows you to use the public and global access modifiers interchangeably?

Are there useful managed package design patterns that involve making a global method public for some subtypes of your class/interface?


Here is an example that the compiler allows:

An abstract class SuperClass

global abstract class SuperClass
{
    global abstract void superClassMethod();    
}

An interface MyInterface

global interface MyInterface{

    void interfaceMethod();

}

A subclass that implements the interface SubClass1

global class SubClass1
       extends SuperClass
       implements MyInterface
       {

    // Note the access modifiers    
    global override void superClassMethod(){ }

    global void interfaceMethod(){ }

}

Another subclass that implements the interface SubClass2

global class SubClass2
       extends SuperClass
       implements MyInterface
       {

    // Note the access modifiers
    public override void superClassMethod(){  }

    public void interfaceMethod(){  }

}

3 Answers 3

2

global and public are essentially synonyms. The only difference is that global methods and interfaces can be accessed outside of the package's code. I can see this as useful in a few situations. Perhaps you want to define an interface that can be used in a global context (e.g. to provide plugin access points for your subscribers), but you also have classes that you want to protect from being called externally that happen to implement these interfaces.

This can be advantageous for two primary reasons: (1) global methods can't be "deleted", so declaring something global might limit future development, and (2) public methods can't be called outside the package. I'm sure someone somewhere out there is using this pattern, but I'd also be willing to bet that this is probably an incredibly rare use case. Without someone at Salesforce explicitly mentioning why the two are interchangeable, the best we can do is guess as to the original intent.

I suspect that this is intentional, though, because it makes sense that you'd want to be able to have a global default implementation yet be able to hide specific sub-implementations that clients shouldn't call directly. Either way, I would carefully consider the possible effects of mixing the two. Generally speaking, if it's not client-callable code, don't make it global. Always provide the minimum access level necessary that works for the intended design.

2
  • I don't have any ideas on how to mix the two annotations at this point, but I am curious what they might be. I was mainly just thinking that an accidental public annotation on a subclass would introduce run-time errors that can only be detected by running code in an installing org, and it would have been nice for the Apex compiler and unit tests in just the managed package developer org to guarantee that these errors wouldn't occur.
    – martin
    Mar 2, 2018 at 12:49
  • @martin As far as I recall, doing so should not introduce errors, but I still wouldn't do it unless I absolutely thought it was necessary.
    – sfdcfox
    Mar 2, 2018 at 13:39
1

I thought of a possible use case, a hook method.

The idea here is that you have a global virtual class that you want the installing orgs to be able to extend, with a global method that has required functionality that you don't want installing orgs to be able to completely overwrite.

When you define the super class, the virtual method needs to be global so that it can be extended by code outside of the namespace:

global virtual class SuperClass{

    global void go(){
        // essential code that needs to be called 
        optionalProcessing();
    }

    global virtual void optionalProcessing(){  }

}

However, if you also define a subclass in the package, you don't want the method to be global since it is already being called by the primary method, and shouldn't be called directly anyways.

global class SubClass
       extends SuperClass
{

    public override void optionalProcessing(){ System.debug(''); }

}
0

The use case that I'm working on right now is the following. We have a framework that consists of a base class with some private and protected (abstract/virtual) methods. Our package contains several classes that extend this framework, and thus override the abstract/virtual methods. Something along the lines of this;

public abstract class Framework_Base {
    protected abstract List<Integer> getCalculationResults();

    public void runAndHandleCalculation(){
        if(!this.checkSecurity()){
            return;
        }
        List<Integer> currentResult = this.getCalculationResults();
        //logic to handle calculation result
    }

    private Boolean checkSecurity(){
        if([we are not allowed]){
            return false;
        }
        return true;
    }
}

public class Framework_ImplementationA {
    protected override List<Integer> getCalculationResults(){
        return new List<Integer>{ //my calculation results };
    }
}

public class Framework_ImplementationB {
    protected override List<Integer> getCalculationResults(){
        return new List<Integer>{ //my calculation results };
    }
}

This works perfectly, we have an SObject in our package that contains class names of classes that implement the base class, which are can be called like this;

Framework_Implemenation__c impl = [SELECT Class__c FROM Framework_Implemenation__c];
Framework_Base inst = (Framework_Base)Type.forName(impl[0].Class__c).getInstance();
inst.runAndHandleCalculation();

Now, we come to realize that we need custom implementations for clients. So, our framework needs to become global, which means the protected abstract method also needs to be global;

global abstract class Framework_Base {
    global abstract List<Integer> getCalculationResults();
    //nothing else changed
}

This results in a compile error in our own implementations, Framework_ImplementationA and Framework_ImplementationB, because they try to reduce the visibility of getCalculationResults(). If Salesforce did not allow us to use public instead of global, we would have to make our own implementations global. Now, we can actually change them to be public instead (which isn't ideal, since we do not want the other classes in the package to access the public methods, but it's far better than having them global).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .