28

I've run into an odd case where a base class is calling a private method in a subclass that isn't defined as an override.

It's a bit contrived, but demonstrates the issue:

public virtual with sharing class Dog {
    public void speak() {
        bark();
    }
    // No virtual modifier, so it can't be overridden. 
    // Private as well. Not Public or Protected
    private void bark() {
        System.debug('The dog says woof');
    }
}

Inherit the base class and provide a new definition of bark()

public with sharing class DogTerrier extends Dog {
    // No override and base method is not virtual or protected/public
    private void bark() {
        System.debug('The terrier says yap');
    }
}

Test it with anonymous Apex in the developer console. All API version are 37.0:

Dog d = new Dog();
d.speak();

DogTerrier t = new DogTerrier();
t.speak();

Dog td = new DogTerrier();
td.speak();

Debug log output:

10:50:44.15 (20036624)|USER_DEBUG|[8]|DEBUG|The dog says woof
10:50:44.15 (20694360)|USER_DEBUG|[4]|DEBUG|The terrier says yap
10:50:44.15 (21226199)|USER_DEBUG|[4]|DEBUG|The terrier says yap

Apex is calling the private method defined in the child class even though the base class isn't virtual and the child classes method isn't doing an override.


For comparison, I did the same test in C#.

public class Dog
{
    public void speak()
    {
        bark();
    }

    private void bark()
    {
        Console.WriteLine("The dog says woof");
    }
}

public class DogTerrier : Dog
{
    private void bark()
    {
         Console.WriteLine("The terrier says yap");
    }
}

And then tested it with a simple console app:

static void Main(string[] args)
{
    Dog d = new Dog();
    d.speak();

    DogTerrier t = new DogTerrier();
    t.speak();

    Console.ReadLine();
}

Console output:

The dog says woof
The dog says woof


For good measure I also tried Java via https://www.compilejava.net/

public class HelloWorld
{
  public static void main(String[] args)
  {
    Dog d = new Dog();
    d.speak();

    DogTerrier t = new DogTerrier();
    t.speak();
  }
}

public class Dog
{
    public void speak()
    {
        bark();
    }

    private void bark()
    {
        System.out.println("The dog says woof");
    }
}

public class DogTerrier extends Dog
{
    private void bark()
    {
         System.out.println("The terrier says yap");
    }
}

The dog says woof
The dog says woof


The Apex behavior seems like a bug to me. It is giving unintended method polymorphism for something that isn't:

  1. virtual
    • The method in the base/super class isn't a candidate for polymorphism.
  2. protected/public
    • The base/super class shouldn't be able to see a private method in the child class.

Raised as Case # 14163020

10
  • Did you try in Java? I believe it should work like C#, but still...
    – sfdcfox
    Jul 8, 2016 at 3:46
  • Just a small note, if we create a interface and try to implement that interface in Dog class SFDC respects overriding principles properly. public virtual interface MyInterface { void bark(); } Jul 8, 2016 at 6:41
  • 5
    @DanielBallinger down that path lies madness. Still, it sounds like fun...
    – sfdcfox
    Jul 8, 2016 at 8:58
  • 3
    We're all mad here.
    – Adrian Larson
    Jul 8, 2016 at 9:43
  • 1
    @PranayJaiswal Last update from support was: "The case is currently escalated to Tier 3 and we are waiting for their investigation" Jul 20, 2016 at 13:42

2 Answers 2

2

The has been logged as a bug and can be tracked publicly via the Knowledge article: A private instance method can be overridden

Summary
If a subclass declares a instance method with the same signature as a private method in one of its superclasses, the private method gets overridden by the subclass' method.

0

Here is my explanation, may not be correct. When you declare the method speak as public it is getting inherited by your child class. Now when you assign as below, Dog td = new DogTerrier(); td holds an instance of DogTerrier, and when speak is invoked it gets a version of bark in that instance and calls that method.

This is how I tried modifying the classes and try.

public virtual with sharing class Dog {
    public virtual void speak() {
        bark();        
    }

    // No virtual modifier, so it can't be overridden. 
    // Private as well. Not Public or Protected
    private void bark() {
        System.debug('The dog says woof');
    }    
}

public with sharing class DogTerrier extends Dog {

    override public void speak(){
        //Calls someother method
        someothermethod();
    }

    // No override and base method is not virtual or protected/public
    //private void bark() {
    //    System.debug('The terrier says yap');
    //}

    // No override and base method is not virtual or protected/public
    private void someothermethod() {
        System.debug('The dalmatian says yoh');
    }
}

My output here comes as

The dog says woof
The dalmatian says yoh
The dalmatian says yoh

And when I try doing this

public with sharing class DogTerrier extends Dog {

    override public void speak(){
        bark();
        //Calls someother method
        someothermethod();
    }

    // No override and base method is not virtual or protected/public
    //private void bark() {
    //    System.debug('The terrier says yap');
    //}

    // No override and base method is not virtual or protected/public
    private void someothermethod() {
        System.debug('The dalmatian says yoh');
    }
}

I get error - Compile Error: Method is not visible: [DogTerrier].bark()

You must log in to answer this question.

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