Object-Oriented X++: Inheritance & Interfaces
Classes, inheritance, abstract classes, interfaces, variable scoping, QueryBuilder, and attributes in X++.
Object-Oriented X++: Inheritance & Interfaces
OOP in X++ works like C# or Java. Classes are blueprints. Inheritance lets children get parent features. Abstract classes cannot be built directly. Interfaces define contracts. QueryBuilder creates dynamic database queries at runtime.
Classes
public class CustomerValidator
{
private str accountNumber;
public void new(str _acct) { accountNumber = _acct; }
public boolean validate()
{
CustTable ct;
select firstOnly ct where ct.AccountNum == accountNumber;
return (ct.RecId != 0);
}
public static CustomerValidator construct(str _acct) { return new CustomerValidator(_acct); }
}
Variable Scoping
| Scope | Lifetime |
|---|---|
| Class member | Object lifetime |
| Method local | Until method returns |
| Block local | Until block ends |
π Sophie: βCarl says keep scope tight β declare variables where they are used.β
Inheritance
public class InvoiceProcessor extends DocumentProcessor
{
public void new(real _amount) { super(); }
public void process() { super(); info("Invoice processed"); }
}
extends = inheritance, super() = call parent, single inheritance only.
Abstract Classes and Interfaces
public abstract class PaymentBase
{
public abstract boolean processPayment(real _amount);
}
public interface IExportable { str exportToJson(); }
public class SalesOrder extends PaymentBase implements IExportable
{
public boolean processPayment(real _amount) { return true; }
public str exportToJson() { return "..."; }
}
| Feature | Abstract Class | Interface |
|---|---|---|
| Method bodies | Mix abstract/concrete | All abstract |
| State | Yes | No |
| Inheritance | Single | Multiple |
| Use when | Shared base behaviour | Contract across classes |
QueryBuilder
Query query = new Query();
QueryBuildDataSource qbds = query.addDataSource(tableNum(CustTable));
qbds.addRange(fieldNum(CustTable, CustGroup)).value("10");
qbds.addSortField(fieldNum(CustTable, Name), SortOrder::Ascending);
QueryRun qr = new QueryRun(query);
while (qr.next())
{
CustTable ct = qr.get(tableNum(CustTable));
info(ct.Name);
}
| Static select | QueryBuilder | |
|---|---|---|
| When | Compile-time fixed | Runtime dynamic |
| Forms | Not for form DS | Used by forms |
| Use | Lookups, logic | Reports, dynamic filters |
Attributes
| Attribute | Purpose |
|---|---|
[DataContractAttribute] | Serializable class |
[DataMemberAttribute] | Include in contract |
[SysEntryPointAttribute] | Service entry point |
[ExtensionOf] | CoC extension |
[DataContractAttribute]
public class ReportContract
{
private date fromDate;
[DataMemberAttribute]
public date parmFromDate(date _v = fromDate) { fromDate = _v; return fromDate; }
}
π¨βπ» Elena: βThe parm pattern β pass a value to set, call empty to get. Every data contract uses it.β
Marcus needs a serializable request class. Attributes?
Dynamic filters based on user input?
Next up: Chain of Command β the most important extensibility pattern.