Extending class state

A new and tremendously powerful feature was introduced in the Fall Release '16. Now you can extend class instances, including adding state. This is available for any class in the system.


We already know we can extend class types. Which in essence allows us to introduce new methods that consumers of the class can benefit from. That was little more than compiler magic; now we got true class extension capabilities.


Suppose you want to extend the SysUserLogCleanup class. Out-of-the-box this class is deleting records from the SysUserLog table. Let's imagine you want to archive these records to a different table before they are deleted.

The SysUserLogCleanup class is a runbase class, so you want to add a check mark to the dialog, get the result of that check box, act on it in the run method, pack/unpack etc. Here is how the state can be extended, and how to act on the dialog() and getFromDialog() methods.

final class mfpSysUserLogCleanup_Extension
    // Extending class state...
    private boolean mfpArchive;
    private DialogField mfpDialogArchive;
    // Adding new instance methods...
    private void mfpDialog(Dialog _dialog)
        mfpDialogArchive = _dialog.addField(extendedtypestr(NoYesId), "Archive");
    private void mfpGetFromDialog()
        mfpArchive = mfpDialogArchive.value();

    // Wiring up event handlers...
    [PostHandlerFor(classStr(SysUserLogCleanup), methodStr(SysUserLogCleanup, dialog))]
    public static void mfpSysUserLogCleanup_Post_Dialog(XppPrePostArgs _args)
        Dialog dialog = _args.getReturnValue();
        SysUserLogCleanup instance = _args.getThis();

    [PostHandlerFor(classStr(SysUserLogCleanup), methodStr(SysUserLogCleanup, getFromDialog))]
    public static void mfpSysUserLogCleanup_Post_GetFromDialog(XppPrePostArgs _args)
        SysUserLogCleanup instance = _args.getThis();  

Please notice, to be a good citizen, I applied these practices:

  • Prefixed the added members and methods. I used "mfp" as prefix. This is important to avoid name clashes with other extensions and future version of the class being extended.
  • Hooked up post-method event handlers for the methods needed.

Other interesting aspects:

  • This also work for forms.
  • This way you can add instance methods to tables – but not state. Table's don't have a class declaration, so that is fair.
  • This way of extending a class will not break the extended class's encapsulation. I.e. you will not have access to any private fields or methods.



Comments (12)

  1. Ripsnan says:

    Hi Michael,

    Is it possible to use “Override method” using class extensions like we do in class inheritance? For example, I have a method named “pdsCWRemainPhysical” in InventMovement class but same method is not overridden in InventMov_Statement class. I would like to override “pdsCWRemainPhysical” method using class extension of Inventmov_Statement class.

    1. Yes – but you have to call next – which as the method is not overridden on InventMov_Statement will call super(), i.e. InventMov.pdsCWRemainPhysical. The code will look like this:

      final class MyInventMov_Statement_Extension
      public PdsCWInventQty pdsCWRemainPhysical()
      return next pdsCWRemainPhysical();

  2. Ripsnan says:

    Root level method at PurchTable form is defined by PurchTable form extension. How to call this method using form instance from the Modified eventhandler of datasource field defined in same extension class?

    1. Hi Ripsnan,

      As forms in X++ are not strongly typed, you’ll have to do a dynamic method call. In X++ the simplest way is to cast the FormRun instance to a variable of type object. The compiler will allow you to invoke any method on object.

      I hope this helps,

      1. Ripsnan says:

        Hi Michael,
        Thanks for your reply. Could you please give a code example?

      2. Here is an example:
        Object obj = myFormRun;

      3. Ripsnan says:

        Hi Michael,
        It worked.Thanks

  3. Marius Gabrijolavicius says:

    This is great feature. With previous version i had to initialize ConditionalWeakTable to link class instances with my class intances. I guess this does pretty much the same under the hood.

  4. This new feature help to handle a clean code and applied best practice to reduce the impact to modified code base.

  5. Ismael says:

    This a new feature that we needed all of the time.

  6. Denis says:

    How you can then use this parameter to achieve your initial task – “want to archive these records to a different table before they are deleted”:? in any case this new parameter will not be visible in SysUserLogCleanup.run()

    1. There are multiple ways to achieve that using eventing – for example by subscribing to the SysUserLogCleanup.onDeleting event.
      I’ll continue on expanding on this example. We also need to cover pack/unpack.

Skip to main content