2

Recently I just saw in some classes something like:

@IsTest
private static void myTest() {
    String a;
    {
        String b = "Something";
    }
    {
        String b = "Something else";
    }
}

I was shocked because I never saw that before. Obviously if I remove the brackets it fails to deploy because of duplicated variable.

I could not find any documentation about this.

What is this used for and what does this exactly do?

2
  • The syntax here is called an initializer. However, it seems inappropriately included here, as no object model is being used and only one block should be used. This syntax has some rare niche purposes, but variable instantiation in a test method is not one of them.
    – Adrian Larson
    Feb 1 '21 at 17:29
  • Thanks! That was the link to the documentation I was looking for Feb 1 '21 at 17:39
13

In Apex and in many other C- and Java-family languages, curly braces define scopes: the boundaries within which variable bindings exist. "Bare" curly braces, without a keyword like if or while to define a control structure around the scope, are also legal.

Functionally, what you're seeing here is code that defines two nested scopes inside the test method. There's almost never a need to do that; it would typically indicate poorly-structure code that needs to be refactored.

You can sort of think of it as if it were written like this.

    if (true) {
        String b = "Something";
    }
    if (true) {
        String b = "Something else";
    }

Both scopes always execute, and have independent variable bindings within them.

3
  • Pretty clear explanation, thanks a lot David! Feb 1 '21 at 17:39
  • 1
    @GabrielSerranoSalas One important thing to know, is that once the code moves past the block, the variables inside are marked as eligible for garbage collection--you can use this to help control your heap usage more carefully if you're running into memory problems.
    – sfdcfox
    Feb 1 '21 at 17:58
  • @sfdcfox Great to know! Thanks for the info! Feb 1 '21 at 19:36
10

To extend @DavidReed's answer, it should be noted that (in Apex and many other languages) variables defined in a block, regardless as to whether it is a method, conditional, iteration, initializer or unguarded block (this is the type of blocks you have since your code snippet has them in a method), go out of scope and cannot be referenced once that block terminates.

There is, however, one scenario where that is not the case in Apex. You can declare a variable in a block and still reference it later in the code flow, outside the block, as a binding in dynamic SOQL (and, I believe, dynamic SOSL).

For example, this strange code will actually work (assuming you have a custom object called SomeObject__c with a string-like field called SomeField__c):

if (someExpression) {
  String bindingA = 'blue harvest';
} else {
  String bindingA = 'something something';
}

List<SomeObject__c> objects = Database.query(
        'SELECT Id From SomeObject__c WHERE SomeField__c = :bindingA');

Interestingly this doesn't actually work with static SOQL - it has to be dynamic.

This happens to work because of the way the Apex "compiler" appears to handle binding variables. From what I can see, the above code basically turns into something like:

String tmpVar_1;

if (someExpression) {
  String bindingA = 'blue harvest';
  tmpVar_1 = bindingA;
} else {
  String bindingA = 'something something';
  tmpVar_1 = bindingA;
}

List<SomeObject__c> objects = Database.query(
        'SELECT Id From SomeObject__c WHERE SomeField__c = :tmpVar_1');

during compilation (a "temporary variable" is declared at the start of the containing function or anonymous apex sequence for each binding and every assignment to the binding variable is instrumented to add an assignment to that "temporary variable" immediately thereafter. It used to be the case you would even see mention of these "temporary variables" in debug logs).

Now, I doubt that this is actually documented anywhere and this isn't typically something you should rely on, but it's an interesting edge case where it comes to apparent variable scope leakage. For some more on this, take a look at this earlier Q&A.

4
  • Really good explanation of this edge case. Thank you very much for taking the time to write such a complete answer :) Feb 1 '21 at 21:15
  • 1
    learned something new .... ironic that this works but xxx.yyy notation doesn't work in dynamic SOQL
    – cropredy
    Feb 1 '21 at 22:43
  • @cropredy likely because they can't annotate assignments to that form of expression and to simplify the query binding parsing.
    – Phil W
    Feb 1 '21 at 22:59
  • 1
    Might be worth linking to this question for reference.
    – Adrian Larson
    Feb 5 '21 at 14:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.