Because collections in Apex have a declared type at runtime, Apex allows collection casting.
Collections can be cast in a similar manner that arrays can be cast in Java. For example, a list of CustomerPurchaseOrder objects can be assigned to a list of PurchaseOrder objects if class CustomerPurchaseOrder is a child of class PurchaseOrder.
public virtual with sharing class PurchaseOrder { }
public with sharing class CustomerPurchaseOrder extends PurchaseOrder { }
List<CustomerPurchaseOrder> CPOs = new List<CustomerPurchaseOrder>(); List<PurchaseOrder> POs = CPOs; // This cast succeeds because POs still has the runtime type List<CustomerPurchaseOrder>. List<CustomerPurchaseOrder> restored = (List<CustomerPurchaseOrder>) POs; // This cast fails at runtime because genericOrders was instantiated as List<PurchaseOrder>. // List<PurchaseOrder> genericOrders = new List<PurchaseOrder>(); // List<CustomerPurchaseOrder> invalid = (List<CustomerPurchaseOrder>) genericOrders;
Once the CustomerPurchaseOrder list is assigned to the PurchaseOrder list variable, it can be cast back to a list of CustomerPurchaseOrder objects, but only because that instance was originally instantiated as a list of CustomerPurchaseOrder objects. A list of PurchaseOrder objects that is instantiated as such cannot be cast to a list of CustomerPurchaseOrder objects, even if the list of PurchaseOrder objects contains only CustomerPurchaseOrder objects.
If the user of a PurchaseOrder list that only includes CustomerPurchaseOrder objects tries to insert a non-CustomerPurchaseOrder subclass of PurchaseOrder (such as InternalPurchaseOrder), a runtime exception results. This is because Apex collections have a declared type at runtime.