28

I have an outer class with an inner class as part of a controller. I have tried setting the instance variables in the outer class as static, which allows the code to compile, but after the controller constructor has executed, subsequent calls to any outer class methods (e.g. from a command button) have nulled out these static instance variables. If I do not set the instance variables in the outer class to static, any references to those variables in the inner class cause a compile error, stating that those variables are not visible:


public class OuterClassStaticExampleController {
   public static String str1;  // remove keyword static and code will not compile
   public OuterClassStaticExampleController() {
      str11 = 'string1';
   }

   public void doneInvokedByCommandButton() {
       String str2 = str1;  // str1 is now null
   }   

   public class InnerClass {
      public String str3;
      public InnerClass() {
         str3 = str1;  // compiles but str1 is null
      }
   }
}
31

If you want your inner class to access outer class instance variables then in the constructor for the inner class, include an argument that is a reference to the outer class instance.

The outer class invokes the inner class constructor passing this as that argument.

public class OuterClass {
  String instanceVbl;


  public void myOuterMethod() {
    InnerClass ic = new InnerClass(this);
    ic.someInnerMethod();
  }
  public class InnerClass {
    OuterClass outer;
    public InnerClass (OuterClass outer) {
       this.outer = outer;
    }
    public void someInnerMethod() {
      String s = outer.instanceVbl + 'appendedStringBits';
      // do something more useful
    }

The example is artificial but illustrates how this can be used to give the Inner Class access to anything the Outer Class instance can reference.

  • That's what I said. Great minds think alike, eh? – sfdcfox Jul 16 '13 at 3:03
  • @sfdcfox - I think we posted at more or less the same time <g> – cropredy Jul 17 '13 at 1:06
13

There is no direct access to outer classes, but you can use a reference (I do this fairly extensively). The pattern looks like:

public class someController {
    public class wrapper {
        someController controller;
        public wrapper(someController con) {
            controller = con;
        }
        public void remove() {
            controller.remove(this);
        }
    }
    public void remove(wrapper item) {
        for(integer i = 0; i < items.size(); i++) {
            if(items[i] == item) {
                items.remove(i);
                break;
            }
        }
    }
    public void add() {
        items.add(new wrapper(this));
    }
    public someController() {
        items = new wrapper[0];
    }
    public wrapper[] items { get; set; }
}

This is a fully functional controller where wrapper can reference someController; it uses that reference to remove itself from a list via a function. Conversely, someController can iterate through items in order to find the relevant instance of a wrapper. It can also access members of the inner class.

Because you can't reference outer classes directly, this method enables your inner classes to communicate with your outer classes.

  • Shouldn't it be controller.remove() instead of con.remove()? – Phil B Feb 19 '14 at 1:19
  • Whoops. Yes, it should. This is what I get for not prototyping in my dev org. – sfdcfox Feb 19 '14 at 4:01
3

Inner classes in Apex are static inner classes so they cannot have references to instance variables of their outer classes which you have observed. There are no non-static inner classes in Apex like there are in some other languages like Java.

You are accessing the static variable of the outer class correctly, but the initialization that you think is happening in the constructor is not.

The reason for str1 not being initialized is that The inner class is not a subclass of the outer class, so the outer class constructor will not run when an instance of the inner class is created, and therefore, str1 will be null. If you want to initialize it you can use a static initializer block.

public static String str1;

static {
    str1 = 'string1';
}

Or just assign it directly:

public static String str1 = 'string1';

See the Static and Instance documentation in the Apex Code Developer's Guide for more information.

  • 1
    Your example misses one small detail; static variables are not stored in the view state, so your method would lose the data through each iteration of controller transactions. – sfdcfox Jul 16 '13 at 2:51
  • Excellent point! If the use case requires storing something in the view state then definitely go the route of maintaining the relationship. – Peter Knolle Jul 16 '13 at 12:03
  • @PeterKnolle looks like your link is broken – Vitaly Zdanevich Jun 1 '16 at 14:07
0
public class Colors {
    public static List <string> color = new List <string>();
    public static void test() {     
        color.add('Red');
        color.add('Green');
        color.add('Blue');
    }
    public static void test2() {
        for(Integer i=0;i<color.size();i++) {
        System.debug(color[i]);
    }
}
}

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.