7

Having recently rolled out a beta version of my open source project, I started looking for ways to optimize it, and accidentally came across the fact that apex actually supports covariant return types in interfaces, but not in classes:

interface IParent {
    IParent get();
}

class Child implements IParent {
    private final String name;
    public Child(String name) {
        this.name = name;
    }
    public Child get() {
        return this;
    }
}

IParent child = new Child('Oleh');
child.get(); // Child:[name=Oleh]

The same example does not work for classes:

abstract [virtual] class Parent {
    abstract [virtual] Parent get();
}
// This does not compile
class Child extends Parent {
    private final String name;
    public Child(String name) {
        this.name = name;
    }
    public override Child get() {
        return this;
    }
}

and produces

Compile failure: Method return types clash: Child vs Parent from the type Child

I don't believe I didn't notice before that covariant return types are supported by interfaces because it's a killer feature. I didn't find anything on this topic in the apex docks. Have covariant return types in interfaces always been available and I just didn't notice it before, or is this a recent feature?

1
  • 1
    Ironically, covariance doesn't work for pure subtypes: interface A extends interface B. Returning a narrower type in B's method runs into a compiler error.
    – identigral
    Commented May 7, 2022 at 0:13

1 Answer 1

6

The covariant return type on interfaces is not directly mentioned in the documentation. It has been around as long as I recall. However, this omission from the docs isn't an accident. It is not fully supported. The reason why it exists at all, I believe, is because Apex used to support generics/custom parameterized classes.

Taking your exact code and copy-pasting in to an execute anonymous script produces:

18:25:13:058 FATAL_ERROR Internal Salesforce.com Error

In other words, you should not depend on this working in all places all the time. In fact, I have logged several bugs over the years regarding various things that should technically work that do not, and there's entire classes of bugs that exist to support some of Apex's "magic" type casts and automatic null-safety features (pre-Safe Navigation Operator).

While it would be really cool to use this, it's not documented and should not be trusted to work correctly in all cases.


Example where the code crashes depending on the org:

Script with Internal Server Error

2
  • 1
    This is interesting because I played around with these examples multiple times exposing them as separate/inner types as well as as a part of anonymous scripts and I never faced FATAL_ERROR Internal Salesforce.com Error. Commented May 10, 2022 at 19:14
  • 1
    @OlehBerehovskyi My guess is that it has to do with the fact that my org has a namespace. I'll try creating a scratch org with a namespace and see if I can replicate the bug. I've edited in an example. That said, there may be other conditions that cause this problem as well.
    – sfdcfox
    Commented May 10, 2022 at 19:47

You must log in to answer this question.

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