4

As documented in What are the pitfalls around Apex Interfaces in managed packages? a global Apex interface that has been published in a managed package can't be modified. Trying to do so results in the compilation error:

Cannot add new abstract methods to interface or virtual or abstract classes:
[List of methods that have been added]

That makes sense, as any subscriber org that implemented the previous global interface wouldn't currently be implementing the new methods.

However, given enough time there may be requirements that require changes to the interface as it is deployed. How can this be handled?

3

This is one possible approach based on extending the original interface.

Initial package definition:

global class FooProvider {
    //V1
    global interface CustomFooProviderV1 {
        string bar();
    }

    public static CustomFooProviderV1 getFooProviderInstance() {
        // Use a CMDT or Custom Setting to get the name of the implementing class
        string className = 'localFooProvider';
        // Use Type.forName and Type.newInstance() to create instance
        //...
        Type t = Type.forName(className);

        // Create an instance to confirm the type
        object testInstance = t.newInstance();

        return (CustomFooProviderV1)testInstance;
    }
}

Sometime later, add the new interface method:

global class FooProvider {
    //V1
    global interface CustomFooProviderV1 {
        string bar();
    }

    //V2
    global interface CustomFooProviderV2 extends CustomFooProviderV1 {
        string raiseTheBar(int height);
    }

    public static CustomFooProviderV2 getFooProviderInstance() {
        // Use a CMDT or Custom Setting to get the name of the implementing class
        string className = 'localFooProvider';
        // Use Type.forName and Type.newInstance() to create instance
        //...
        Type t = Type.forName(className);

        // Create an instance to confirm the type
        object testInstance = t.newInstance();
        if(testInstance instanceOf CustomFooProviderV2) {
           return testInstance;  
        }
        if(testInstance instanceOf CustomFooProviderV1) {
           return new FooUpgrader((CustomFooProviderV1)testInstance);
        }

        return null;   
    }

    public class FooUpgrader implements CustomFooProviderV2 {
        private CustomFooProviderV1 v1;
        public FooUpgrader(CustomFooProviderV1 v1Param) {
            v1 = v1Param;
        }
        public string bar() {
            return 'foobar';
        }
        public string raiseTheBar(int height) {
            throw new InterfaceVersionException('Method raiseTheBar is not supported with an implementation of the v1 FooProvider interface');
        }
    }
    public class InterfaceVersionException extends Exception {}
}

Other alternatives include:

  1. create an entirely new interface that just covers the new methods.
  2. Have different versions of getFooProviderInstance that target specific API versions. Then the wrapper Upgrader class wouldn't be required and the caller wouldn't need to handle the potential exceptions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.