Visibility for Apex in Managed Packages

apex visibility in managed packages
Now that I’m starting to spend time playing with packaging code for use I decided to dig into how access modifiers affect visibility of methods and classes inside of managed packages.

Visibility Access Modifiers

Before we get started, let’s review what options we have for defining visibility in Apex

private – Methods, classes and variables marked as private are only visible inside the same class.  If you define something as private then it cannot be accessed from an external class

public – Things that are marked as public are available for use by anything in the same namespace.

global – Things marked as global are available for use by anything on the platform.

Typically, public and private are enough for most implementations since your code resides inside the same namespace.  When writing code to be used by others from your managed package you’ll want to make it global.

Visibility Examples

So let’s take a look an example class that shows how visibility restricts access

global class VisibilityTest {
    global class InnerClass {
        private InnerClass() {
            System.debug(System.LoggingLevel.ERROR, 'PRIVATE: constructor');
        }
        
        public InnerClass(String input) {
            this();
            System.debug(System.LoggingLevel.ERROR, 'PUBLIC: constructor ' + input);
        }
        
        global InnerClass(String input, Integer i) {
            this(input);
            System.debug(System.LoggingLevel.ERROR, 'GLOBAL: constructor ' + input + ' : ' + i);
        }
        
        global void globalPrintClass(String input) {
            System.debug(System.LoggingLevel.ERROR, 'GLOBAL: ' + input);
        }
        
        public void publicPrintClass(String input) {
            System.debug(System.LoggingLevel.ERROR, 'PUBLIC: ' + input);
        }
        
        private void privatePrintClass(String input) {
            System.debug(System.LoggingLevel.ERROR, 'PRIVATE: ' + input);
        }
        
        global void testPrintClass(String input) {
            globalPrintClass(input);
            publicPrintClass(input);
            privatePrintClass(input);
        }
    }
    
    global static void globalPrint(String input) {
        System.debug(System.LoggingLevel.ERROR, 'GLOBAL: ' + input);
    }
    
    public static void publicPrint(String input) {
        System.debug(System.LoggingLevel.ERROR, 'PUBLIC: ' + input);
    }
    
    private static void privatePrint(String input) {
        System.debug(System.LoggingLevel.ERROR, 'PRIVATE: ' + input);
    }
    
    global static void testPrint(String input) {
        globalPrint(input);
        publicPrint(input);
        privatePrint(input);
    }
}

In our class above we have several methods of different access levels as well as a class with constructors and methods of different visibility.

Static Methods

Let’s start with our static methods

VisibilityTest.testPrint('Package Org');
VisibilityTest.globalPrint('Package Org');
VisibilityTest.publicPrint('Package Org');
VisibilityTest.privatePrint('Package Org');

If we were try to run the code above we’ll get an error because the method privatePrint is not visible

Line: 4, Column: 1
Method is not visible: pcon_test1.VisibilityTest.privatePrint(String)

After removing the privatePrint line, we can see the output we get in the debug log

06:32:02.3 (19931064)|USER_DEBUG|[37]|ERROR|GLOBAL: Package Org
06:32:02.3 (20013061)|USER_DEBUG|[41]|ERROR|PUBLIC: Package Org
06:32:02.3 (20078051)|USER_DEBUG|[45]|ERROR|PRIVATE: Package Org
06:32:02.3 (20182423)|USER_DEBUG|[37]|ERROR|GLOBAL: Package Org
06:32:02.3 (20257988)|USER_DEBUG|[41]|ERROR|PUBLIC: Package Org

With the first call of testPrint we can see that we have access all three inner methods.  This is because the testPrint method belongs to the class and is allowed to call any private methods.  Then we’ll see the other two calls that were made.

Class Constructors

For our class we have three different constructors that call from most visible to least visible.

VisibilityTest.InnerClass ic1 = new VisibilityTest.InnerClass();
VisibilityTest.InnerClass ic2 = new VisibilityTest.InnerClass('ic2');
VisibilityTest.InnerClass ic3 = new VisibilityTest.InnerClass('ic3', 3);

If we try to run the code above we’ll get an error because of the empty constructor is marked as private and we do not have access to it.

Line: 1, Column: 33
Constructor is not visible: [VisibilityTest.InnerClass].<Constructor>()

After removing the private constructor line, we can see the output we get in the debug log

06:35:28.1 (4796769)|USER_DEBUG|[4]|ERROR|PRIVATE: constructor
06:35:28.1 (4873272)|USER_DEBUG|[9]|ERROR|PUBLIC: constructor ic2
06:35:28.1 (5354334)|USER_DEBUG|[4]|ERROR|PRIVATE: constructor
06:35:28.1 (5394937)|USER_DEBUG|[9]|ERROR|PUBLIC: constructor ic3
06:35:28.1 (5525878)|USER_DEBUG|[14]|ERROR|GLOBAL: constructor ic3 : 3

The constructor for ic2 calls the private constructor and prints out the our debug message.  The same occurs with the global constructor as it calls the public and private constructor.

Class Methods

Now that we’ve looked at constructors the same visibility applies for the class methods

VisibilityTest.InnerClass ic = new VisibilityTest.InnerClass('ic');
ic.testPrintClass('inner class');
ic.globalPrintClass('inner class');
ic.publicPrintClass('inner class');
ic.privatePrintClass('inner class');

As with the static methods, the code above fails with the privatePrintClass method

Line: 5, Column: 1
Method is not visible: [VisibilityTest.InnerClass].privatePrintClass(String)

Removing that method give us the following output

06:39:03.1 (3789730)|USER_DEBUG|[4]|ERROR|PRIVATE: constructor
06:39:03.1 (3890473)|USER_DEBUG|[9]|ERROR|PUBLIC: constructor ic
06:39:03.1 (4264832)|USER_DEBUG|[18]|ERROR|GLOBAL: inner class
06:39:03.1 (4414994)|USER_DEBUG|[22]|ERROR|PUBLIC: inner class
06:39:03.1 (4563225)|USER_DEBUG|[26]|ERROR|PRIVATE: inner class
06:39:03.1 (4741124)|USER_DEBUG|[18]|ERROR|GLOBAL: inner class
06:39:03.1 (4904861)|USER_DEBUG|[22]|ERROR|PUBLIC: inner class

As with our static methods, any method inside of our class has access to the private method so testPrintClass is allowed to call the private method.

Manage Package Visibility

Now that we’ve looked at the visibility of the code inside the same namespace, let’s package this code up and install it in another org.  You can follow along by installing this package in your developer org.

Reviewing Methods

Class visibility

Thankfully the package provides a class summary to show which methods and constructors we have access to in our package.

Static Methods

So let’s take look at the code that worked in our packaging org.  We’ll first want append our namespace to our class name.

pcon_test1.VisibilityTest.testPrint('Client Org');
pcon_test1.VisibilityTest.globalPrint('Client Org');
pcon_test1.VisibilityTest.publicPrint('Client Org');

However, when we try to run this, we get the following error

Line: 3, Column: 1
Method is not visible: pcon_test1.VisibilityTest.publicPrint(String)

This is because we are outside the namespace of the class so we’ll have to remove the public method call.  After removing that our code works.

NOTE: Because this is inside a managed package we cannot see the debug messages.

Class Constructors

Same as before, we try to run the two constructors that worked

pcon_test1.VisibilityTest.InnerClass ic2 = new pcon_test1.VisibilityTest.InnerClass('ic2');
pcon_test1.VisibilityTest.InnerClass ic3 = new pcon_test1.VisibilityTest.InnerClass('ic3', 3);

But we’ll fail with the following error

Line: 1, Column: 44
Constructor is not visible: [pcon_test1.VisibilityTest.InnerClass].<Constructor>(String)

Again, this is because the constructor is public and not inside the same namespace as the package

Class Methods

And lastly we’ll take a look at the class level methods

pcon_test1.VisibilityTest.InnerClass ic = new pcon_test1.VisibilityTest.InnerClass('ic', 0);
ic.testPrintClass('inner class');
ic.globalPrintClass('inner class');
ic.publicPrintClass('inner class');

As with the other examples, we cannot access the public method because we are not in the packages namespace.

Line: 4, Column: 1
Method is not visible: [pcon_test1.VisibilityTest.InnerClass].publicPrintClass(String)

Summary

For most code that’s written, you don’t need to deal with global methods and variables.  However when dealing with stuff that needs to be visible to consumers of your packages you’ll want to pay attention to your visibility settings.

This entry was posted in Development, Salesforce and tagged , , . Bookmark the permalink.