Platform bug: for Map<IKey, V>, containsKey(lookupKey) can return false even when lookupKey.hashCode() matches a key in the map and both lookupKey.equals(existingKey) and existingKey.equals(lookupKey) are true.
Root cause: when the map’s key type is an interface, the platform may not call the key’s equals() during containsKey() (effectively using reference equality instead).
% aer test force-app
PASS MapEqualsDispatchBugTest.containsKeyWithAbstractMiddle
Ran 1 tests: 1 passed, 0 failed
% sf apex run test -o test-31gzyr326ogv@example.com --synchronous
› Warning: @salesforce/cli update available from 2.121.7 to 2.124.7.
=== Test Results
TEST NAME OUTCOME MESSAGE RUNTIME (MS)
────────────────────────────────────────────────────── ─────── ────────────────────────────────────────────────────────────────────────────────────────────────────────────── ────────────
MapEqualsDispatchBugTest.containsKeyWithAbstractMiddle Fail System.AssertException: Assertion Failed: Hypothesis: containsKey with interface→abstract→concrete (StringKey)
Class.MapEqualsDispatchBugTest.containsKeyWithAbstractMiddle: line 22, column 1
=== Test Summary
NAME VALUE
─────────────────── ─────────────────────────────
Outcome Failed
Tests Ran 1
Pass Rate 0%
Fail Rate 100%
Skip Rate 0%
Test Run Id 707Au00002YjYS5
Test Setup Time 0 ms
Test Execution Time 61 ms
Test Total Time 61 ms
Org Id 00DAu00000FNHxBMAX
Username test-31gzyr326ogv@example.com
| Class | Purpose |
|---|---|
IKey |
Interface declaring equals, hashCode, toString. |
AbstractKey |
Abstract class implementing IKey; defines equals/hashCode (with System.debug so you can see if they’re called). |
StringKey |
Concrete key extending AbstractKey (interface → abstract → concrete). |
MapEqualsDispatchBugTest |
Single test that puts one key and checks containsKey with a second, equal key. |
The test builds a Map<IKey, String>, puts k1 = new StringKey('hello'), then creates k2 = new StringKey('hello'). It asserts that k1 and k2 have the same hashCode and that k2.equals(existing) and existing.equals(k2) for the map’s key. Then it asserts m.containsKey(k2). When the bug occurs, that assertion fails and AbstractKey.equals() never appears in the Debug Log for the containsKey path.
Run the test class MapEqualsDispatchBugTest. If the bug reproduces:
- The assertion
m.containsKey(k2)fails. - In the Debug Log,
AbstractKey.equals() calleddoes not appear for thecontainsKeycall (you may see it for the manualk2.equals(existing)/existing.equals(k2)checks).
- Key the map by
String(e.g.key.toString()) so the platform uses string equality, or - Find the key by iterating (match
hashCode()then call yourequals()), then use the found key forget()instead of relying oncontainsKey(lookupKey).