Is Apex Casting is not transitive?

You can't hop more than one data type per cast; each additional conversion requires another cast.

For example, the following works:

Integer i = (Integer)5.2;

But you can't write:

Object o = 5.2;
Integer i = (Integer)o;

And you also can't write:

Integer i = (Integer)(Object)5.2;

Basically, casting a Decimal from an Object reference to an Integer reference is invalid, because it's not a compatible type.

I wouldn't call this a "bug" so much as a limitation of the runtime. It's not documented, so I'm going to ask about it elsewhere.

For now, though, you can safely use Integer.valueOf to get a valid conversion, even if the value is null.


Edit:

The same behavior is observed in Java, so at least it's consistent.

Runtime Error (Java)

    Object a = 5.2f;
    int b = (int)a;

Valid Code (Java)

    float a = 5.2f;
    int b = (int)a;

It appears that this limitation stems from Java itself, and is not specific to Apex Code.


In Salesforce My_Field__c we create field with Number actually stores the decimal type values even if we define decimal places as 0. It is never be an Integer until we transform the value explicitly from Decimal to Integer.

Decimal myDec = (Decimal) mySObject.get('My_Field__c');

So, then from this Decimal we can transform to Integer.

Also, refer Rules of Conversion

Numbers form a hierarchy of types. Variables of lower numeric types can always be assigned to higher types without explicit conversion. The following is the hierarchy for numbers, from lowest to highest:

  1. Integer
  2. Long
  3. Double
  4. Decimal

Once a value has been passed from a number of a lower type to a number of a higher type, the value is converted to the higher type of number.


It's not really a bug, because the returned value is not an instance of an Integer. Here's a snippet to demonstrate:

SObject record = [SELECT Integer__c FROM MyObject__c LIMIT 1];
Object value = record.get('Integer__c');
system.debug(value);
system.debug(value instanceOf Integer);
system.debug(value instanceOf Long);
system.debug(value instanceOf Double);
system.debug(value instanceOf Decimal);

The resulting log:

123
false
false
true
true

However, you don't need to use casting at all. Simply use Integer.valueOf:

SObject record = [SELECT Integer__c FROM MyObject__c LIMIT 1];
Integer value = Integer.valueOf(record.get('Integer__c'));