8

I have a use case where I will have an Apex Class wrapping an sObject, and I need to be able to swap out certain internal functionality based on certain conditions. My plan is to use an few inner classes that implement a common interface, and select them accordingly. To do this, I need to expose certain details of the outer class only to the child classes. This seems like a perfect use case for protected methods, per the docs (empahsis mine):

protected

This means that the method or variable is visible to any inner classes in the defining Apex class, and to the classes that extend the defining Apex class. You can only use this access modifier for instance methods and member variables. Note that it is strictly more permissive than the default (private) setting, just like Java.

However, trying to do this gives the error "New protected methods cannot be defined in non-virtual classes." Seems pretty clear, except that the error contradicts one of the documented use cases; also, I can add protected properties to the outer class without an error.

Here's a simplified class which omits the multiple child classes but which demonstrates the issue:

public class OuterClass {

  //this compiles ok if I remove "protected" from getChildren below
  protected CustomObject__c obj {get; set;}

  public OuterClass(CustomObject__c o) {
    this.obj = o;
  }

  // this method should only be called by Inner Class BusinessLogic
  // this is where the compilation error occurs
  protected list<ChildObject__c> getChildren() {
    return obj.ChildObjects__r;
  }

  public decimal Calculate() {
    BusinessLogic bl = new BusinessLogic(this);
    return bl.calculate();
  }

  private class BusinessLogic {
    OuterClass out;

    public BusinessLogic(OuterClass oc) {
      this.out = oc;
    }

    public decimal Calculate() {
      decimal result = 0;
      for (ChildObject__r child : out.getChildren()) {
        result += child.Value__c;
      }
      return result;
    }

  }

}

I've seen this question about the same error but that only addresses protected virtual methods. I've also seen this question indicating that inner classes can access private members of the outer class, but that doesn't seem to express my intent. I could make OuterClass virtual, except that it isn't actually virtual. I'll probably just change to private and be done with it, but I'm wondering if I'm overlooking something? Seems inconsistent that I can make a property protected (including custom getter/setter if needed) but not a method. Does this seem like an Apex bug or a Documentation bug? Would like a second opinion before I submit an issue.

6

You can use a much simpler MVR here:

public class Demo
{
    protected String variable;
    protected void method() { }
    public String property { get; protected set; }
}

This behavior does seem to be a bug, since you can declare protected properties. Either both should work, or neither. I'll try opening a support case and see what they say. Especially interesting is that getters/setters, which exist in somewhat of a grey area, can be protected in a concrete class.

Plenty of the text you did not emphasize lends credence to this belief (that there's a bug).

... to the classes that extend the defining Apex class.

This portion of the description vaguely implies that all protected accessibility is intended for classes which can be extended, which would mean you shouldn't be able to use this modifier for properties on concrete classes either.

You can only use this access modifier for instance methods and member variables.

To my eye, this portion of the documentation implies the behavior should be the same for both.

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.