Skip to content

ChuckJonas/wtfapex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

49 Commits

Repository files navigation

What the f*ck, Apex?

A list of funny and tricky Apex examples

Inspired bywtfjs

📖Table of Contents

✍🏻 Notation

// ->is used to show the result of an expression. For example:

1+1;// -> 2

// >means the result ofSystem.debugor another output. For example:

System.debug('hello, world!');// > hello, world!

//!Means a runtime exception was thrown:

Decimald=1/0;//! Divide by 0

// ~Code fails to compile:

Decimald='foo';// ~ Illegal assignment from String to Decimal

//is just a comment used for explanations. Example:

// Assigning a function to foo constant
Foofoo=newFoo();

👀Examples

When a boolean is not a boolean

Booleanb;
if(b!=true)system.debug('b is not true');//> b is not true
if(b!=false)system.debug('b is not false');//> b is not false
if(b){}//!Attempt to de-reference a null object

SeeAdvanced Apex Programming in Salesforcefor explanation.

String compare is case-insensitive (except when it's not)

Stringx='Abc';
Stringy='abc';
System.assert(x==y);
System.assertEquals(x,y);//! Expected: Abc, Actual: abc

Seeexplanation on StackExchange

Objectequalsoverride

Try tooverridetheequalsmethod on any class and you'll be greeted with a very unexpected compile error:@Override specified for non-overriding method.

However, just remove theoverridekeyword and it compiles!

At first glance it might seem like this works, but it is in fact very broken:(

publicclassMyClass{
publicBooleanequals(Objectother) {
System.debug('Called my equals');
returnfalse;
}
}

MyClassm=newMyClass();
Objecto=newMyClass();

m.equals('a');// > 'Called my equals'
o.equals('a');// System.debug is never called:(

Source:Aidan Harding

Shadowing System (global) classes

Nothing prevents you from recreating a class with the same name of one that exists in theSystem(default) namespace.

publicclassDatabase{
publicstaticList<sObject>query(Stringqry) {
System.debug(qry);
returnnull;
}
}

RunningDatabase.query('foo')will call our new class (essentially overriding theDatabase methods)!?

The same principle also applies to standard SObjects:

publicclassAccount{ }

Accountacc=newAccount();
acc.AccountNumber='123';//! Variable does not exist: AccountNumber

Source:Daniel Ballinger

"Phantom" Inner Class Type Equivalency

publicclassIExist{}
System.assertEquals(IExist.class,IExist.IDont.Class);// -> passes

Source:Kevin Jones

Listcontains&indexOfis broken

"Apex Log level" influences behavior

The easiest way to test this is by writing the output to an object field.

  1. ReplaceaccIdwith a dummy account
  2. Open a dev console and set the log levels toApex=Finest
  3. Run the following code
IdaccId='0012F00000YIc48QAD';
Accountacc=newAccount(Id=accId);
List<Id>haystack=newList<Id>();
haystack.add('0012F00000YIc46QAD');
haystack.add(accId);
haystack.add('0012F00000YIc49QAD');
Stringdebug='Index: '+haystack.indexOf(needle) +' Contains: '+haystack.contains(needle);
acc.AccountNumber=debug;
updateacc;
  1. Open the account. You should see theAccountNumberequalsIndex: 1 Contains: true
  2. Set log levels toApex=None
  3. Run the code again
  4. Refresh the account.AccountNumberwill now equalIndex: -1 Contains: false

Apparently this is aknown issue and it has been fixed.This test shows otherwise...

15 Char Id's don't work

Behind the scenes Salesforce seems to always convert 15 character Id's to 18.

Equivalency works as expected in most cases:

Ida15='0012F00000YIc48';
Ida18='0012F00000YIc48QAD';
System.assert(a15==a18);

However, for the Listcontains&indexOfmethods, it doesn't:

List<Id>idList=newList<Id>{
'0012F00000YIc46',
'0012F00000YIc48',
'0012F00000YIc49'
};
System.debug(idList);//-> (0012F00000YIc46QAD, 0012F00000YIc48QAD, 0012F00000YIc49QAD)
System.debug(idList.indexOf('0012F00000YIc48'));// > -1
System.debug(idList.contains('0012F00000YIc48'));// > false

You can avoid this by first assigning the value you are checking to anIdtype.

finalparameters "exist", but can be reassigned

You won't find a reference to it in the docs, but the compiler does apparently allowfinalparameters.However, it doesn't actually to prevent reassignment of such parameters:

publicvoidwithFinalParam(finalStringiAmFinal){
iAmFinal='just kidding';//compiles
}

Source:Kevin Jones

Fulfilling Interface Contracts with Static Methods

This shouldn't work but it does. Apparently also works with batch.

publicclassStaticsAreCoolimplementsSchedulable{
publicstaticvoidexecute(SchedulableContextsc){
}
}

Source:Kevin Jones

Exceptions are "exceptional"

In their naming conventions:

publicclassOhGodBeesextendsException{}
// ~ Classes extending Exception must have a name ending in 'Exception'

and their Constructors:

publicclassBeesExceptionextendsException{
publicBeesException(){}
}
// ~ System exception constructor already defined: <Constructor>()

For explanation and further interesting observations,see Chris Peterson's blog post.

Systemcan have ambiguous return types

Database.queryis one of many cases where the SalesforceSystemnamespace doesn't play by its own rules. It can return either aList<SObject>or a singleSObject.No casting required.

Foo__cfoo=Database.Query('SELECT Id FROM Foo__c');
List<Foo__c>foos=Database.Query('SELECT Id FROM Foo__c');

Try writing your own method to do this and you'll get an error:

Method already defined: query SObject Database.query(String) from the type Database (7:27)

You can overload arguments, but notreturntype.

Odd List Initialization bug

The initialization syntax forList<T>expectsT... args.

So obviously, if you passed aList<T>into it, you will get compile error:

List<Task>{newList<Task>()};// ~ Initial expression is of incorrect type, expected: Task but was: List<Task>

Except, if List comes fromnew Map<Id,T>().values()...

The following code compiles without issue!

newList<Task>{newMap<Id,Task>().values()};

To add to the perplexity, when executed you will receive the following runtime error:

System.QueryException: List has no rows for assignment to SObject

Source: Matt Bingham

Local Scope Leak

If you write an If/Else without braces, symbols scoped in the "if" seem to leak into the "else":

if(false)
Stringa='Never going to happen.';
else
a='I should not compile';

Worth noting that Java won't even allow you to declare a block scoped variable inside a "braceless IF" as it can never be referenced elsewhere.

Source:Kevin Jones

Broken type inference forSet<>

Let's take a look at the standardSetclass...

It can be iterated in a foreach loop:

Set<String>mySet=newSet<String>{'a','b'};
for(Strings:mySet){}

But, according to Salesforce (compiler & runtime), it does not actually implement theIterableinterface:

String.join(mySet,',');// ~ "Method does not exist or incorrect signature: void join(Set<String>, String)..."

// Just to make sure, lets check at runtime..
System.debug(mySetinstanceofIterable<String>);// > false

Except... It actually does:

String.join((Iterable<String>)mySet,',');// this works!?

Vote to fix this

String.Format with single quotes

Stringwho='World';
Stringmsg=String.format(
'Hello, \'{0}\'',
newList<String>{who}
);
System.assert(msg.contains(who));//! assertion failed

Unexpectedly,msgis set toHello, {0}

🤔

To get this to work properly you must escapetwosingle quotes:

Stringwho='World';
Stringmsg=String.format(
'Hello, \'\'{0}\'\'',
newList<String>{who}
);
System.assert(msg.contains(who));// -> passes

Explanation by Daniel Ballinger

Line continuation breaks for static method

In apex, all statements must be terminated by a;.This allows statements to span multiple lines:

Ordero=newOrderBuilder()
.addLineItem('foo',5)
.addLineItem('bar',10)
.setDiscount(0.5)
.toOrder();

However, for some reason if the method is static, apex doesn't let it span a newline:

Ordero=OrderBuilder
.newInstance()// ~ Variable does not exist: OrderBuilder
.addLineItem('foo',5)
.addLineItem('bar',10)
.setDiscount(0.5)
.toOrder();

Source:Leo Alves

Fun with Hashcodes

Enums in batch

Objects in hashcodes

JSON Serialization

  1. There's no way to control automatic serialization of object properties (like[JsonProperty(PropertyName = "FooBar" )]in C#)
  2. There are reserved keywords that you can't use as property names.

Meaning the following cannot be parsed or generated usingJSON.deserializeorJSON.serialize:

{
"msg":"hello dingus",
"from":"Dr. Dingus"
}

Work-around

Generics (parameterized interfaces) exist, but you can't use them

Once upon a time, generics were actually part of Apex. However, they have since been removed (with the exception of system classes (List<>,Batchable<>,etc).

Why would you want generics when your OS has perfectly good Copy & Paste functionality built right into it?

Vote for Generics

Polymorphic Primitives

Objectx=42;
System.debug(xinstanceOfInteger);//> true
System.debug(xinstanceOfLong);//> true
System.debug(xinstanceOfDouble);//> true
System.debug(xinstanceOfDecimal);//> true

Source:Daniel Ballinger

Invalid HTTP method: PATCH

When you try this:

Httph=newHttp();
HttpRequestreq=newHttpRequest();
req.setEndpoint('hooli ');
req.setMethod('PATCH');
HttpResponseres=h.send(req);//! Invalid HTTP method: PATCH

There is a workaround,but only supported by some servers.

🔧 Since Fixed

Thankfully, these WTF's have since been fixed by Salesforce. We'll keep them documented for historical purposes (and entertainment).

Mutating Datetimes

https://twitter /FishOfPrey/status/869381316105588736

More hashcode fun

https://twitter /FishOfPrey/status/1016821563675459585

https://salesforce.stackexchange /questions/224490/bug-in-list-contains-for-id-data-type

Initializing Abstract Classes

Resolved in Spring '20 by the "Restrict Reflective Access to Non-Global Controller Constructors in Packages"Critical Update

publicabstractclassImAbstract{
publicStringfoo;
}

ImAbstractorAmI= (ImAbstract)JSON.deserialize('{ "foo": "bar" }',ImAbstract.class);
System.debug(orAmI.foo);// > bar

SeeStack Exchange Post

About

A list of funny and tricky Apex examples

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published