The Secret Life of an SObject: Equality, Sets and Maps

Equality

When testing for equality between sObjects in Apex, it is the sObjects’ properties – the values of the sObjects’ fields – which are compared. So two separate sObject instances that have the same field values are considered equal:

Account foo1 = new Account(), foo2 = new Account();
foo1.Name = 'bar';
foo1.AnnualRevenue = 12345;
foo2.Name = 'bar';
foo2.AnnualRevenue = 12345;
system.assert(foo1 == foo2);

But, if we were to mimic our sObject using a custom Apex type, we would find that by default, two instances with the same property values would not be considered equal:

MyAccount foo1 = new MyAccount(), foo2 = new MyAccount();
foo1.Name = 'bar';
foo1.AnnualRevenue = 12345;
foo2.Name = 'bar';
foo2.AnnualRevenue = 12345;
system.assert(foo1 != foo2);

public class MyAccount
{
   public String Name;
   public Decimal AnnualRevenue;
}

See Method Declarations in Anonymous Apex

This is because with Apex classes we have to decide how we want equality to be evaluated, by defining our own equals method.

If we don’t define an equals method then the equality operator will compare object references (memory locations) – in other words asking “are these references to the exact same instance?”

If we wish, we can make the same reference comparison with sObjects too, using the exact equality and exact inequality operators.

Account foo1 = new Account(), foo2 = new Account();
foo1.Name = 'bar';
foo1.AnnualRevenue = 12345;
foo2.Name = 'bar';
foo2.AnnualRevenue = 12345;
system.assert(foo1 !== foo2);
foo2 = foo1;
system.assert(foo1 === foo2);

We cannot however define our own equals method for sObjects. Continue reading

Posted in Documentation | Tagged , , | 6 Comments

Apex Method of the Day – String myString.split(String regExp)

We can split a String into parts using a token, which in this case is a space:

String str = 'Foo Bar Force';
List<String> parts = str.split(' ');
system.assertEquals(3, parts.size());
system.assertEquals('Foo', parts[0]);
system.assertEquals('Bar', parts[1]);
system.assertEquals('Force', parts[2]);

However, the following example, using “.” as a token will fail:

String str = 'Foo.Bar.Force';
List<String> parts = str.split('.');
system.assertEquals(3, parts.size());

System.AssertException: Assertion Failed: Expected: 3, Actual: 0

This is because the token is actually a regular expression, and some characters, like “.” have a special meaning within a regular expression. These special characters will need to be escaped with a backslash if they are to be treated literally – as is our intention.

However, by adding a backslash the following will actually fail to compile:

String str = 'Foo.Bar.Force';
List<String> parts = str.split('\.');
system.assertEquals(3, parts.size());

Invalid string literal ‘\.’. Illegal character sequence ‘\.’ in string literal

Backslash itself is a special character in Apex String literals, and so it needs to be further escaped with an additional backslash. Finally, the following gives the desired result:

String str = 'Foo.Bar.Force';
List<String> parts = str.split('\\.');
system.assertEquals(3, parts.size());

You have to think of this as a two stage process. In the first stage Apex interprets the literal string, in the second stage the regular expression is evaluated from the results of the first stage.

The Apex Code Developer’s Guide provides an interesting example where we want to split the string using the backslash character itself. The backslash is escaped – giving us 2 – and then each backslash also is escaped – giving us 4:

List<String> parts = filename.split('\\\\');

See also:
Force.com Apex Code Developer’s Guide – String Methods
Salesforce StackExchange – Bug in String.split(‘.’)?

Posted in Documentation | Tagged , | 1 Comment

Apex Method of the Day – String myString.repeat(numTimes)

public class TestUtility
{
   static Integer s_num = 1;

   public static String getFakeId(Schema.SObjectType sot)
   {
      String result = String.valueOf(s_num++);
      return sot.getDescribe().getKeyPrefix() + 
         '0'.repeat(12-result.length()) + result;
   }
}

Force.com Apex Code Developer’s Guide – String Methods

Posted in Documentation | Tagged , | 1 Comment

Apex Method of the Day – Method (and other) Declarations in Anonymous Blocks

An anonymous block is Apex code that does not get stored in the metadata, but that can be compiled and executed using… Developer Console, Force.com IDE [or] The executeAnonymousSOAP API call

A method can be defined and called within an Anonymous Block:

Decimal d1 = 123;
Decimal d2 = 456;
system.debug(multiply(d1,d2));

Decimal multiply(Decimal val1, Decimal val2)
{
   return val1 * val2;
}

An exception can be defined and thrown within an Anonymous Block:

if(true) throw new AnonymousException('Foo!');
system.debug('We will not reach here');

class AnonymousException extends Exception {}

A class can be defined and instantiated within an Anonymous Block:

Mean m = new Mean();

for(Account item : [select AnnualRevenue from Account where AnnualRevenue<>null limit 100])
   m.add(item.AnnualRevenue);

system.debug(m.get());

class Mean
{
   Decimal total = 0;
   Decimal count = 0;

   void add(Decimal value)
   {
      total+=value;
      count++;
   }

   Decimal get()
   {
      return total/count;
   }
}

Force.com Apex Code Developer’s Guide – Anonymous Blocks

Posted in Documentation | Tagged , | 2 Comments

The Secret Life of an SObject: Defaults

Default values, dirty field tracking, checkboxes / booleans, and the Winter’13 loadDefaults argument…

When creating custom objects, we can define default values for our fields which are automatically applied when inserting records.

Defaulted when?

Say we have a custom Sales Order object with an Order Date field that defaults to today’s date. To get some visibility of what’s going on during the insertion process, we’ll use a simple trigger:

trigger SalesOrder on SalesOrder__c (before insert)
{
    for(SalesOrder__c item : trigger.new)
        system.debug( 'Trigger Before: ' + item.OrderDate__c );
}

Now we insert a record via some Anonymous Apex:

SalesOrder__c salesOrder = new SalesOrder__c();
system.debug('New: ' + salesOrder.OrderDate__c);
insert salesOrder;
system.debug('Inserted: ' + salesOrder.OrderDate__c);
salesOrder = [select OrderDate__c from SalesOrder__c
    where Id=:salesOrder.Id];
system.debug('Reloaded: ' + salesOrder.OrderDate__c);

USER_DEBUG|[2]|DEBUG|New: null
USER_DEBUG|[4]|DEBUG|Trigger Before: 2013-06-03 00:00:00
USER_DEBUG|[4]|DEBUG|Inserted: null
USER_DEBUG|[7]|DEBUG|Reloaded: 2013-06-03 00:00:00

As we can see from the debug log, the default value is applied to our record some time between submitting the DML insert and the trigger being executed. Continue reading

Posted in Documentation | Tagged , , , | Leave a comment

LockingRules

Recently I was working on a prototype Force.com application for which I anticipated heavy use of Salesforce Approvals Processes. When testing my prototype, I found the record locking system with Approvals fell short of what I wanted – I needed to be able to lock specific fields rather than the record as a whole.

My Approvals Process made use of a “stage” pickist provided in the prototype App, and I realised that I could achieve what I wanted by using validation rules in conjunction with this stage field. However, the validation rules would be fairly complex, and not particularly easy to modify if the business requirements changed.

What I wanted was an easy way for users to configure field locking based on a stage / status field on a record, for any object type… LockingRules.

Continue reading

Posted in Utilities | Tagged , , , , , | 1 Comment

Apex Calls Between Independent Packages

You have a great idea for a process “Orchestrator” utility that can coordinate processes across your existing Force.com products, and perhaps involve some third-party apps too. Orchestrator may want to create, read, update and delete records within these products, but because of its process-oriented nature it will need to initiate existing product processes that have been exposed as Apex APIs.

Your customers may want to use Orchestrator with a variety of product combinations – so they won’t all have the same packages installed. But there’s a problem. Making a call in Apex from one package (Orchestrator) to APIs (Apex methods) in your product packages will create a dependency between the packages:

2013-02-05-DependentPackages

If Orchestrator has the ability to work with any of your products then all of your products must be installed in order to install Orchestrator.

This is not ideal. Orchestrator needs to call the Apex APIs within the other packages, but it also has to be independent of the other packages.

Continue reading

Posted in Patterns | Tagged , , , , | 1 Comment