1

I had the impression that when we overload, it will run with the most specific type(Date for example). In the following example, it always runs with least specific type (Object):

String.valueOf((true)?(Date)sObject.get(fieldName):sObject.get(fieldName));
//this will print 2018-11-22 00:00:00

However, if we do not use inline:

String oldValue;
if (true){
  oldValue = String.valueOf((Date)sObject.get(fieldName));
} else {
  oldValue = String.valueOf(sObject.get(fieldName));
}

//this will print 2018-11-22

Could someone provide more background on this topic?

1

Your ternary is returning the compile-time type of Object. So, this is the expected behaviour of calling methods with overloaded parameters. This resolution is done at compile time.

From the Java spec (I don't know of a similar Apex spec)

If the compile-time declaration for the method invocation is a signature polymorphic method, then [...] The compile-time parameter types are the types of the actual argument expressions.

From https://docs.oracle.com/javase/specs/jls/se13/html/jls-15.html#jls-15.12.3

This may be confusing because calling virtual methods overridden in a subclass is resolved at run-time.

In code, the following is true:

@IsTest
private class OverloadingTest {

    enum Result {
        BASE_OBJECT,
        BASE_STRING,
        SUBCLASS_OBJECT,
        SUBCLASS_STRING
    }

    private virtual class Base {
        virtual Result f(Object o) {
            return Result.BASE_OBJECT;
        }
        virtual Result f(String s) {
            return Result.BASE_STRING;
        }
    }

    private class Subclass extends Base {
        private override Result f(Object o) {
            return Result.SUBCLASS_OBJECT;
        }

        private override Result f(String s) {
            return Result.SUBCLASS_STRING;
        }

    }

    @IsTest
    static void testBehaviour() {
        Base base = new Base();
        Base subclassAsBase = new Subclass();
        String s = 's';
        Object sAsObject = (Object)s;

        System.assertEquals(Result.BASE_OBJECT, base.f(sAsObject));
        System.assertEquals(Result.BASE_STRING, base.f(s));

        System.assertEquals(Result.SUBCLASS_OBJECT, subclassAsBase.f(sAsObject));
        System.assertEquals(Result.SUBCLASS_STRING, subclassAsBase.f(s));

    }
}
  • Thanks for this detailed answer! I was expecting this answer since String.valueOf((true)?(Date)sObject.get(fieldName):(Date)sObject.get(fieldName)); was calling String.valueOf(Date). However, I got confused when I tested an overriden method. – kvor Nov 26 '19 at 13:27
1

The problem is that you use String.valueOf() for ternary operator, not for Date exactly. The result of ternary operator will be Object, however, if you type something like this:

system.debug(true ? String.valueOf((Date) sObject.get(fieldName)) : String.valueOf(sObject.get(fieldName))); //2019-11-26

everything works fine.

  • Thanks. I was trying to find the reason of this behaviour, overloading happens on compile time as @Aidan explained. – kvor Nov 26 '19 at 13:33

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.