[SalesForce] Is this ternary operator logic documented

Ever since… well, the dawn of Apex Code, I've always presumed that the ternary operator evaluated both the true and false branches as part of its operation. Today, while playing around with them, I realized that only one branch is evaluated.

As a trivial example:

Integer a() {
    System.debug('a was called');
    return 5;
}
Integer b() {
    System.debug('b was called');
    return 10;
}
Integer c = Math.random()>=0.5? a(): b();

However, I was rather pleasantly surprised when I found that there was only one debug statement being called.

Going one step further, I then tested this on a set of queries:

Account a = Math.random()>=0.5?
    [SELECT Name FROM Account WHERE Name='Foo']:
    [SELECT Name FROM Account WHERE Name='Bar'];
System.assertEquals(1, Limits.getQueries());

This actually worked; only one query was used, as the other wasn't even evaluated.

In other words, ternary operators are exactly as described:

Ternary operator (Right associative). This operator acts as a short-hand for if-then-else statements. If x, a Boolean, is true, y is the result. Otherwise z is the result. Note that x cannot be null.

They're actually short-hand for if-then-else, even down to the fact that only one branch is evaluated!

However, the documentation doesn't explicitly call this out that I can tell. Can we rely on this behavior? Is there a place where this behavior is explicitly documented?

Best Answer

I'm not sure what you're looking for, as the documentation seems to tell us exactly what the behavior should be, and that matches up with what we've observed. These two methods should be equivalent:

Integer x = 42, y = 0;
Integer evaluateTernary(Boolean input)
{
    return input ? x : y;
}
Integer evaluateIfThenElse(Boolean input)
{
    if (input) return x;
    else return y;
}

If you want to hold Salesforce to it, you could install this test class as a canary:

@IsTest
class TernaryContract
{
    class UnexpectedEvaluationException extends Exception {}

    static Id evaluate() { return UserInfo.getUserId(); }
    static Id noEvaluate()
    {
        throw new UnexpectedEvaluationException();
        return evaluate();
    }

    static testMethod void testTruthyEvaluation()
    {
        Test.startTest();
            Id evaluation = true ? evaluate() : noEvaluate();
        Test.stopTest();

        system.assertEquals(UserInfo.getUserId(), evaluation,
                            'The truthy value should be used');
    }
    static testMethod void testFalsyEvaluation()
    {
        Test.startTest();
            Id evaluation = false ? noEvaluate() : evaluate();
        Test.stopTest();

        system.assertEquals(UserInfo.getUserId(), evaluation,
                            'The truthy value should be used');
    }
}
Related Topic