Generic sObjects - Reading Fields
Not Started

You saw the power of dynamic code in the last lesson. Let's push this idea a little further. Say you have this in your codebase:

public class sObjectHandler { public void mainMethod(List<Account> accounts, List<Opportunity> opportunities){ SObjectUtils utils = new SObjectUtils(); List<String> accountRatings = utils.getAccountRatings(accounts); List<String> opportunityNames = utils.getOpportunityName(opportunities); } } public class SObjectUtils { private List<String> getAccountRatings(List<Account> accounts){ List<String> accountRatings = new List<String>(); for(Account acc : accounts){ accountRatings.add(acc.Rating); } return accountRatings; } private List<String> getOpportunityName(List<Opportunity> opportunities){ List<String> opportunityNames = new List<String>(); for(Opportunity o : opportunities){ opportunityNames.add(o.Name); } return opportunityNames; } }

There's a pattern here, right? Each method accepts a sObject list and hardcodes the desired String field inside. To avoid this repetition, we can pass in the field as a parameter along with a list of generic sObjects. This will give us a single method that can be called for each unique combination of sObject and String fields:

public class sObjectHandler { public void mainMethod(List<Account> accounts, List<Opportunity> opportunities){ SObjectUtils utils = new SObjectUtils(); List<String> accountRatings = utils.getStrings(accounts, 'Rating'); List<String> opportunityNames = utils.getStrings(opportunities, 'Name'); } } public class SObjectUtils { private List<String> getStrings(List<sObject> records, String fieldToRetrieve){ List<String> values = new List<String>(); for(Sobject record : records){ String value = record.fieldToRetrieve; values.add(value); } return values; } }

Except that won't compile. The 2nd parameter fieldToRetrieve is the name of the field represented as a String, so we can't use the dot operator to retrieve the field. Dang. Pack your bags, time to go home. Lesson over. Learn Java instead I guess...

Sike! Our dreams are not dead, and you don't have to waste your time learning Java. We just need some extra syntax to make this work. The sObject class has a method called get() that we can use to make our code more generic. get() is an alternative for the dot operator. It's an overloaded method that can accept either a String that matches the API name of the field like Rating or a field token like Schema.Account.Rating.

There's just one small problem. Once called, get() returns a system class type called Object. It doesn't return the matching datatype of the field. Code like this can't compile:

public String getAccountName(Account acc){ String accountName = acc.get('Name'); return accountName; }

We're attempting to store the result of acc.get('Name') which is of type Object into accountName which is of type String. We'll be hit with this compilation error: Illegal assignment from Object to String.

To make this work, we have to cast the result of acc.get('Name'). Casting is when we convert the datatype of one class into the datatype of another class. This only works when the two datatypes have a parent/child (or superclass/subclass) relationship. We'll explain what that means in the OOP course. Thankfully, Object is the parent datatype of primitives like Strings, Boolean, Date, and others.

To cast, we'd need to declare the type we wish to cast to by wrapping the name of the type in parentheses:

public String getAccountName(Account acc){ String accountName = (String) acc.get('Name'); // casting the Object into a String return accountName; }

See, not that bad. Now, instead of using the dot notation, let's plug this line of code into the for loop we had before:

public class sObjectHandler { public void mainMethod(List<Account> accounts, List<Opportunity> opportunities){ SObjectUtils utils = new SObjectUtils(); List<String> accountRatings = utils.getStrings(accounts, 'Rating'); List<String> opportunityNames = utils.getStrings(opportunities, 'Name'); } } public class SObjectUtils { private List<String> getStrings(List<sObject> records, String fieldToRetrieve){ List<String> values = new List<String>(); for(Sobject record : records){ String value = (String) record.get(fieldToRetrieve); values.add(value); } return values; } }

Voila! Now we have one method that can extract any string field of any sObject!

Challenge

The calculateSumOfDecimals method is partially built. The method accepts a list of generic sObjects and the name of a decimal field as a String. The method should iterate through each sObject and calculate the sum of the field.

The method will be called like this:

Opportunity o1 = new Opportunity(Amount='5.00'); Opportunity o2 = new Opportunity(Amount='5.50'); List<Opportunity> opportunities = new List<Opportunity>{o1, o2}; SObjectUtils utils = new SObjectUtils(); Decimal total = utils.calculateSumOfDecimals(opportunities, 'Amount'); // total should be 10.50