8.3 Interfaces, Polymorphism & Dynamic Codeunit Invocation

Key Takeaways

  • Interfaces in AL define pure syntactic contracts of procedure signatures, enabling object-oriented polymorphism and adherence to the Open-Closed Principle in Business Central.
  • Codeunits implement interfaces using the 'implements' keyword, requiring exact compile-time signature conformity for parameter types, reference directions (var), and return types.
  • Extensible enums link interface definitions to concrete codeunits via the 'Implementation' property, enabling dynamic runtime instantiation without hardcoded CASE branching.
  • The DefaultImplementation and UnknownValueImplementation enum properties provide robust fallback mechanisms for uninitialized or unmapped ordinal values.
  • A single codeunit can implement multiple interfaces simultaneously, and interfaces facilitate isolated unit testing by enabling mock implementations without external network dependencies.
Last updated: August 2026

8.3 Interfaces, Polymorphism & Dynamic Codeunit Invocation

Prior to the introduction of interfaces in AL, writing extensible business logic with multiple interchangeable implementations required extensive CASE statements or complex hook events. If a third-party extension wanted to add a new calculation method, shipping carrier, or payment gateway, it had to subscribe to hook events and handle custom branching manually. AL interfaces enable object-oriented polymorphism by decoupling the definition of capabilities from their concrete execution, allowing dynamic runtime dispatch governed by extensible enums.


1. Interface Declaration & Syntax

An interface object in AL defines a contractual specification composed of procedure signatures. It contains no implementation code, no local or global variable declarations, and no triggers. It enforces a strict compile-time contract: any codeunit declaring that it implements the interface must provide concrete implementations for every procedure with identical parameter names, data types, parameter passing modes (var vs. value), and return types.

// 1. Interface Declaration
interface "IShippingCarrier"
{
    procedure CalculateShippingCost(PackageWeight: Decimal; DestinationPostalCode: Code[20]): Decimal;
    procedure GenerateTrackingNumber(ShipmentHeaderNo: Code[20]): Text[50];
    procedure ValidateAddress(AddressLine: Text[100]; PostalCode: Code[20]; CountryCode: Code[10]): Boolean;
}

Rules for Interface Declarations

  1. Procedure Signatures Only: Each declaration consists of the procedure name, input parameters, and optional return value. No begin...end blocks or variable blocks are permitted.
  2. Access Modifiers: Procedures declared within an interface are implicitly public contracts.
  3. Versioning Stability: Once an interface is published in an AppSource app or shared library, altering existing procedure signatures breaks all dependent codeunits. New capabilities should be introduced via distinct interface extensions or new interface objects.

2. Implementing Interfaces in Codeunits

Codeunits implement interfaces by specifying the implements keyword followed by the quoted interface name. Multiple distinct codeunits can implement the same interface, each encapsulating provider-specific business logic.

// 2. Concrete Implementation: FedEx Carrier
codeunit 50120 "FedEx Shipping Provider" implements "IShippingCarrier"
{
    procedure CalculateShippingCost(PackageWeight: Decimal; DestinationPostalCode: Code[20]): Decimal
    begin
        // FedEx rate calculation algorithm / API call
        exit(15.0 + (PackageWeight * 1.75));
    end;

    procedure GenerateTrackingNumber(ShipmentHeaderNo: Code[20]): Text[50]
    begin
        exit('FDX-' + Format(CurrentDateTime, 0, '<Year4><Month,2><Day,2><Hours24><Minutes,2><Seconds,2>'));
    end;

    procedure ValidateAddress(AddressLine: Text[100]; PostalCode: Code[20]; CountryCode: Code[10]): Boolean
    begin
        // FedEx address validation service
        exit((PostalCode <> '') and (CountryCode <> ''));
    end;
}

// 3. Concrete Implementation: UPS Carrier
codeunit 50121 "UPS Shipping Provider" implements "IShippingCarrier"
{
    procedure CalculateShippingCost(PackageWeight: Decimal; DestinationPostalCode: Code[20]): Decimal
    begin
        // UPS zone-based calculation algorithm
        exit(12.5 + (PackageWeight * 2.10));
    end;

    procedure GenerateTrackingNumber(ShipmentHeaderNo: Code[20]): Text[50]
    begin
        exit('1Z999999' + ShipmentHeaderNo);
    end;

    procedure ValidateAddress(AddressLine: Text[100]; PostalCode: Code[20]; CountryCode: Code[10]): Boolean
    begin
        // UPS address validation logic
        exit(StrLen(PostalCode) >= 5);
    end;
}

Multi-Interface Implementation on a Single Codeunit

AL supports implementing multiple interfaces on a single codeunit. This allows a unified service codeunit to satisfy several distinct operational contracts across the system.

interface "ILabelPrinter"
{
    procedure PrintShippingLabel(TrackingNo: Text[50]; LabelFormat: Code[10]): Boolean;
}

// Codeunit implementing both IShippingCarrier and ILabelPrinter
codeunit 50122 "DHL Express Provider" implements "IShippingCarrier", "ILabelPrinter"
{
    // IShippingCarrier implementation
    procedure CalculateShippingCost(PackageWeight: Decimal; DestinationPostalCode: Code[20]): Decimal
    begin
        exit(18.0 + (PackageWeight * 1.50));
    end;

    procedure GenerateTrackingNumber(ShipmentHeaderNo: Code[20]): Text[50]
    begin
        exit('DHL-' + ShipmentHeaderNo);
    end;

    procedure ValidateAddress(AddressLine: Text[100]; PostalCode: Code[20]; CountryCode: Code[10]): Boolean
    begin
        exit(PostalCode <> '');
    end;

    // ILabelPrinter implementation
    procedure PrintShippingLabel(TrackingNo: Text[50]; LabelFormat: Code[10]): Boolean
    begin
        // Direct thermal / PDF label generation logic
        exit(true);
    end;
}
Loading diagram...
Enum-Driven Polymorphism & Dynamic Interface Dispatch Architecture

3. Dynamic Runtime Instantiation via Extensible Enums

The full architectural power of AL interfaces is realized when coupled with extensible enums. By mapping enum values directly to implementing codeunits, the AL runtime performs dynamic polymorphic instantiation without requiring brittle branching logic.

enum 50120 "Shipping Carrier Type" implements "IShippingCarrier"
{
    Extensible = true;
    DefaultImplementation = "IShippingCarrier" = "Manual Carrier Provider";
    UnknownValueImplementation = "IShippingCarrier" = "Manual Carrier Provider";

    value(0; Manual)
    {
        Caption = 'Manual / Local Delivery';
        Implementation = "IShippingCarrier" = "Manual Carrier Provider";
    }
    value(1; FedEx)
    {
        Caption = 'FedEx Express';
        Implementation = "IShippingCarrier" = "FedEx Shipping Provider";
    }
    value(2; UPS)
    {
        Caption = 'UPS Ground';
        Implementation = "IShippingCarrier" = "UPS Shipping Provider";
    }
    value(3; DHL)
    {
        Caption = 'DHL International';
        Implementation = "IShippingCarrier" = "DHL Express Provider";
    }
}

Eliminating Fragile CASE Statements

In traditional AL programming, selecting an algorithm based on an enum or option field required a CASE statement. If a third-party extension added a new option, the base CASE statement failed or ignored the new value. With interfaces, developers cast the enum variable directly to the interface variable:

codeunit 50130 "Shipment Execution Engine"
{
    procedure ProcessShipmentFreight(CarrierType: Enum "Shipping Carrier Type"; Weight: Decimal; PostalCode: Code[20]): Decimal
    var
        Carrier: Interface "IShippingCarrier";
        FreightAmount: Decimal;
    begin
        // Dynamic runtime instantiation: The runtime creates the matching codeunit automatically!
        Carrier := CarrierType;
        
        FreightAmount := Carrier.CalculateShippingCost(Weight, PostalCode);
        exit(FreightAmount);
    end;
}

Adding New Providers in Independent Extensions

A separate ISV extension can add support for a new carrier (e.g., Royal Mail) without modifying the base app:

  1. Declare a codeunit codeunit 50200 "Royal Mail Provider" implements "IShippingCarrier".
  2. Extend the enum: enumextension 50200 "Royal Mail Ext" extends "Shipping Carrier Type".
  3. Add value value(50200; RoyalMail) { Implementation = "IShippingCarrier" = "Royal Mail Provider"; }.
  4. When the user selects RoyalMail on a shipping method, Carrier := CarrierType automatically resolves to Royal Mail Provider at runtime.

4. Fallback Implementations & Test Mocking

DefaultImplementation & UnknownValueImplementation

To prevent unhandled runtime exceptions when data is incomplete or corrupted, AL enums support two critical fallback properties:

  • DefaultImplementation: Specifies the fallback codeunit to instantiate when an uninitialized enum variable (ordinal 0 or unassigned) is cast to an interface.
  • UnknownValueImplementation: Specifies the fallback codeunit when an unrecognized or unmapped ordinal value is encountered (for example, if a third-party extension that added an enum value is uninstalled, but existing records still contain that integer value in the database).
enum 50125 "Tax Engine Type" implements "ITaxCalculator"
{
    Extensible = true;
    DefaultImplementation = "ITaxCalculator" = "Standard Tax Engine";
    UnknownValueImplementation = "ITaxCalculator" = "Standard Tax Engine";

    value(0; Standard)
    {
        Implementation = "ITaxCalculator" = "Standard Tax Engine";
    }
    value(1; Avalara)
    {
        Implementation = "ITaxCalculator" = "Avalara Tax Engine";
    }
}

Testing with Mock Interface Implementations

Interfaces are invaluable for automated unit testing in Business Central. By implementing a mock codeunit that satisfies an interface contract, developers can test complex business logic without connecting to live external web services or incurring third-party API costs.

codeunit 50140 "Mock Carrier Provider" implements "IShippingCarrier"
{
    procedure CalculateShippingCost(PackageWeight: Decimal; DestinationPostalCode: Code[20]): Decimal
    begin
        // Return deterministic test data
        if DestinationPostalCode = '99999' then
            exit(0.0); // Free shipping test scenario
        exit(25.0);
    end;

    procedure GenerateTrackingNumber(ShipmentHeaderNo: Code[20]): Text[50]
    begin
        exit('MOCK-TRACK-12345');
    end;

    procedure ValidateAddress(AddressLine: Text[100]; PostalCode: Code[20]; CountryCode: Code[10]): Boolean
    begin
        exit(true);
    end;
}

Architectural Comparison: Interfaces vs. Events vs. CASE Statements

FeatureAL Interfaces & EnumsEvent-Driven HooksLegacy CASE Statements
Extensibility ModelOpen-Closed Principle (additive enum extensions).Decoupled subscribers listening to publishers.Closed monolithic branching.
Contract EnforcementStrict compile-time signature verification.Weak (parameter mismatches cause runtime issues).None (hardcoded branches).
Execution PatternExactly one concrete implementation per enum value.Multiple subscribers execute in non-deterministic order.Single branch matches ordinal.
Fallback HandlingNative DefaultImplementation & UnknownValue.Manual handling via if not IsHandled then.else branch required.
Unit TestabilityHigh (swap enum with mock implementation).Moderate (manual binding required).Poor (requires mock database data).
Test Your Knowledge

A developer writes: 'codeunit 50100 MyCarrier implements ICarrier, ILabelPrinter'. What does the AL compiler require of this codeunit?

A
B
C
D
Test Your Knowledge

In an extensible Enum that implements an Interface, what is the primary purpose of declaring the 'UnknownValueImplementation' property?

A
B
C
D
Test Your Knowledge

A developer needs to create an extensible calculation engine where different ISV apps can provide their own calculation algorithms. Which AL language constructs must be combined to achieve compile-time contract enforcement and dynamic runtime execution without CASE statements?

A
B
C
D
Test Your Knowledge

How do AL interfaces facilitate automated unit testing when developing integrations with third-party payment gateways?

A
B
C
D