This is some WTFApex magic related to dynamic SOQL queries.
There is another similar example where you can do some funky implicit casting with SOQL queries and Database.Query('soql');
:
Foo__c foo = Database.Query('SELECT Id FROM Foo__c');
List<Foo__c> foos = Database.Query('SELECT Id FROM Foo__c');
Note how it will happily give you either a single sObject or a collection.
Lets expand your example out to see where it might break:
List<SObject> randomsObjects = new List<SObject>{new Account(), new Contact()};
List<Account> accounts = randomsObjects;
for(Account acc : accounts) {
// Ruh-roh! That Contact isn't an Account sObject
System.debug(acc.Name);
}
This results in the following error on the for
statement:
System.QueryException: Dynamic query yields incompatible SObject type Contact for loop variable of type Account
So it appears Apex is aware of the potential problems at runtime and associates it back with Dynamic SOQL queries as being the primary source of this occurring.
This also relates to the batch example that Phil W gave.
A pet example I have is if you have a Database.Batchable implementation you can declare it like public with sharing class MyBatch implements Database.Batchable<SObject>
and then implement the execute method templated on a different type, like public void execute(Database.BatchableContext context, List<Account> scope)
, without any complaints.
In your Database.Batchable
start method it is possible to return a Database.QueryLocator that is constructed from a SOQL string. Apex can't confirm the resulting sObject type until runtime, but allows you to declare that the batch is going to execute over a fixed sObject type.
public with sharing class MyBatch implements Database.Batchable<SObject>
and then implement the execute method templated on a different type, likepublic void execute(Database.BatchableContext context, List<Account> scope)
, without any complaints.)