编写 Apex 代码:数据类型、集合、变量与运算符完全参考

Apex 语言核心完全参考:涵盖所有数据类型(原始类型、sObject、集合)、List/Set/Map 三种集合的完整用法与注意事项、多行字符串、枚举与系统枚举、变量声明与命名规则、Null 处理与作用域、常量、安全导航运算符(?.)与空值合并运算符(??)的完整说明、运算符优先级表、赋值语句与类型转换规则,以及溢出和除法精度等重要注意事项。...

📅 2026/7/16 ✍️ ponybai 🏷️ apex, salesforce

编写 Apex 代码

编写 Apex 代码

Apex 是 Salesforce 的编程语言,类似于 Java。它允许你在 Lightning Platform 持久层中添加和操作数据。Apex 使用类、数据类型、变量和 if-else 语句,你可以基于条件执行代码,或者让一段代码重复执行。本章是 Apex 语言核心参考,涵盖所有基础构建块:数据类型、集合、枚举、变量、表达式和运算符。

编写 Apex 代码

数据类型概述

在 Apex 中,所有变量和表达式都有一个数据类型。类型检查在编译时严格执行——解析器会在类型不匹配时生成包含行号和列号的错误信息。

Apex 支持的数据类型包括:

  • 原始类型(Primitive):Integer、Double、Long、Date、Datetime、String、ID、Boolean、Decimal、Blob、Time、Object
  • sObject:通用 sObject 或特定 sObject(如 Account、Contact、MyCustomObject__c)
  • 集合(Collection):List(有序、可重复)、Set(无序、唯一)、Map(键值对)
  • 枚举(Enum):具有有限标识符集的类型化值列表
  • 用户定义的 Apex 类和系统提供的 Apex 类
  • Null:null 常量,可以赋值给任何变量
Apex 数据类型

原始数据类型

Apex 使用与 SOAP API 相同的原始数据类型(高精度 Decimal 类型在某些情况下例外)。所有 Apex 变量(无论是类成员变量还是方法变量)都初始化为 null。请确保在使用前将变量初始化为适当的值。例如,将 Boolean 变量初始化为 false

原始数据类型

核心原始类型概览

类型描述
Blob以单个对象存储的二进制数据集合。可通过 toString()valueOf() 方法与 String 互转。可用于文档、附件和 Web 服务参数。
Boolean值只能为 truefalsenull。始终在使用前初始化为 false。
Date表示特定日期,不含时间信息。始终使用系统静态方法创建日期值。可与 Integer 进行加减运算。
Datetime表示特定日期和时间(时间戳)。始终使用系统静态方法创建。可与 Integer 或 Double 进行加减运算。
Decimal任意精度的数字,包含小数点。货币字段自动分配为 Decimal 类型。使用 setScale() 设置小数位数。

更多原始类型

类型描述
Double64 位浮点数。最小值 -2^63,最大值 2^63-1。不支持科学计数法(e)。
ID18 字符的 Salesforce 记录标识符。设置为 15 字符值会自动转换为 18 字符。无效 ID 值会引发运行时异常。
Integer32 位整数。范围:-2,147,483,648 到 2,147,483,647。
Long64 位整数。范围:-2^63 到 2^63-1。字面量使用 L 后缀:Long l = 2147483648L;
Object任何 Apex 支持的数据类型。所有 Apex 数据类型都继承自 Object。可强制转换为其底层数据类型。
String单引号包围的字符集。大小限制由堆大小限制决定。sObject String 字段值不能为空字符串(只能为 null)且不能包含首尾空格。支持与 SOQL 相同的转义序列。
Time表示特定时间。始终使用系统静态方法创建。

String 特别说明:与 Java 不同,Apex Strings 支持比较运算符(==、!=、<、>、<=、>=),比较基于上下文用户的语言环境且不区分大小写。

多行字符串(Multiline Strings)

Apex 支持使用三个单引号(''')定义多行字符串:

String str = '''
  第一行
  第二行
  第三行
  ''';

关键规则:

  • 换行:换行符自动转换为结果字符串中的换行序列
  • 空白:字符串最左侧非空白字符之前的所有空白都会被去除;每行的尾部空白也会被去除。空白去除在编译时进行,在转义序列处理之前
  • 单引号:支持不转义的单引号(')。但在闭合的三个单引号之前必须转义:\''''

转义序列与拼接

  • 使用 \s 在行尾保留尾部空白
  • 使用 \ 在行尾拼接多行而不插入换行符:
String str = '''
这是一行不能在一行写完 \ 但我不希望它包含换行 \
所以使用了转义序列
''';

SOQL 中的限制:多行字符串变量可用于 SOQL/SOSL 查询,但多行字符串字面量不能直接用在 SOQL/SOSL 查询中(在绑定表达式中除外)。

版本化行为变更

  • API 16.0+:Apex 在货币等类型中使用更高精度的 Decimal
  • API 15.0+:如果为字段赋值的 String 值太长,Apex 类和触发器会产生运行时错误

还有两个非标准原始类型不能在变量中使用但出现在系统方法中:AnyType(用于字段历史跟踪表)和 Currency(仅用于 SOQL/SOSL WHERE 子句)。

集合:List、Set 和 Map

集合在 Apex 中可以是 List(列表)、Set(集合)或 Map(映射)。集合没有项目数量限制,但受堆大小一般限制。

集合类型

List(列表)—— 有序、可重复

List 是有序的元素集合,通过索引区分。List 元素可以是任何数据类型。第一个元素的索引位置始终为 0。

创建语法:

List<datatype> listName = new List<datatype>();
List<datatype> listName = new List<datatype>{v1, v2};

数组表示法(一维列表):

String[] colors = new List<String>();
// 等效于:
String[] colors = new String[1];

colors[0] = 'Green';  // 基于索引的访问
重要:使用方括号添加元素时,列表行为类似于数组(不可弹性扩展)。使用 List 的 add() 方法可实现弹性增长。

排序:List.sort() 对原始类型按升序排序。自定义类型可实现 Comparable 接口或传入 Comparator。列表最多可嵌套七层(总共八层)。

List 列表 List 排序

Set(集合)—— 无序、唯一

Set 是无序的元素集合,不允许重复。Set 没有索引位置的概念,你只能迭代遍历 Set 元素。

Set<Integer> mySet = new Set<Integer>();
mySet.add(1);
mySet.add(3);
System.assert(mySet.contains(1));
mySet.remove(1);

特点:

  • 使用哈希结构(无 HashSet/TreeSet 区分)
  • 迭代顺序是确定性的(每次执行相同)
  • 常见模式:收集唯一 ID 用于 SOQL WHERE IN 子句
Set 集合

Map(映射)—— 键值对

Map 是键值对的集合,每个唯一键映射到一个值。键必须唯一——使用已存在的键 put 新值会覆盖旧值。

Map<Integer, String> m = new Map<Integer, String>();
m.put(1, 'First entry');
m.put(2, 'Second entry');
String value = m.get(2);  // 返回 'Second entry'
Boolean has = m.containsKey(4);  // false

快速初始化:

Map<String, String> MyStrings = new Map<String, String>
    {'a' => 'b', 'c' => 'd'.toUpperCase()};
Map 映射

Map 注意事项

  • 所有 Map 使用哈希结构(无 HashMap/TreeMap 区分)
  • 迭代顺序是确定性的,但建议始终通过键访问
  • String 键区分大小写!'Key' 和 'key' 是不同的键
  • Map 键可以持有 null 值
  • 用户定义类型键的唯一性由 equals()hashCode() 方法决定
  • sObject 键注意事项:sObject 变更后不再映射到相同的值
  • JSON 序列化:Map 仅在使用特定键类型(Boolean、Date、DateTime、Decimal、Double、Enum、Id、Integer、Long、String、Time)时才能序列化为 JSON
Map 注意事项

参数化类型

Apex 是静态类型语言——在使用变量之前必须声明其数据类型。List、Map 和 Set 使用参数化类型,在构造时用实际数据类型替换占位符:

List<String> myList = new List<String>();

子类型关系:如果类型 T 是 U 的子类型,则 List<T>List<U> 的子类型。这意味着你可以将 List<String> 赋值给 List<Object>

参数化类型

枚举(Enum)

枚举是一种抽象数据类型,其值从你指定的一组有限标识符中取一个。枚举通常用于定义没有数值顺序的可能值集合,例如卡牌的花色或一年中的季节。

public enum Season {WINTER, SPRING, SUMMER, FALL}
Season current = Season.SUMMER;

特点:

  • 每个值对应一个独特的整数,但实现被隐藏(防止算术误用)
  • 与 Java 不同,枚举类型本身没有构造器语法
  • 可以将类定义为枚举(不使用 class 关键字)
  • 任何 webservice 方法都可以使用枚举类型——WSDL 文件将包含枚举及其值的定义
枚举

系统定义的枚举

Apex 提供以下系统枚举:

  • System.StatusCode —— 对应 WSDL 中公开的 API 错误代码
  • System.LoggingLevel —— 用于 System.debug() 的日志级别
  • System.RoundingMode —— 数学运算的舍入行为(如 Decimal divide、Double round)
  • System.SoapType —— 字段描述结果 getSoapType() 的返回值
  • System.DisplayType —— 字段描述结果 getType() 的返回值
  • System.JSONToken —— 用于解析 JSON 内容
  • System.ApplicationReadWriteMode —— 指示组织在升级期间是否处于只读模式
  • ApexPages.Severity —— Visualforce 消息的严重程度
系统定义的枚举不能在 Web 服务方法中使用。所有枚举值(包括系统枚举)都有通用方法,但不能向枚举值添加用户定义的方法。
系统枚举

变量

局部变量使用 Java 风格语法声明:

Integer i = 0;
String str;
List<String> strList;
Set<String> s;
Map<String, String> m;

// 单语句声明多个变量
Integer i, j, k;
变量声明

变量命名规则

  • 变量名不区分大小写
  • 只能包含字母(A-Z、a-z)、数字(0-9)和下划线(_),不能包含空格或特殊字符
  • 必须以字母开头,不能以数字或下划线开头
  • 不能以下划线结尾
  • 不能包含连续下划线(__)
  • 不能使用 Apex 保留关键字
  • 最大长度:255 个字符

Null 变量与变量作用域

如果声明变量但未初始化,其值为 null。常见陷阱:未初始化的 Boolean 变量是 null,不是 false!

Boolean x = null;
Decimal d;  // 隐式为 null
Date d; d.addDays(2);  // 抛出 NullPointerException!

变量作用域:变量可以在代码块内的任何位置定义,并从该点开始生效。子代码块不能重新定义父代码块中已使用的变量名,但并行代码块可以重用变量名。

Null 变量与作用域

常量

常量是初始化后值不会改变的变量,使用 final 关键字定义。final 关键字意味着变量最多可以被赋值一次,可以在声明中直接赋值,也可以通过静态初始化器方法赋值:

public class myCls {
  static final Integer PRIVATE_INT_CONST = 200;
  static final Integer PRIVATE_INT_CONST2;

  public static Integer calculate() { return 2 + 7; }

  static {
    PRIVATE_INT_CONST2 = calculate();
  }
}
常量

表达式与运算符

表达式是由变量、运算符和方法调用组成的构造,求值结果为单个值。表达式可以通过运算符连接形成复合表达式。

表达式与运算符

表达式类型

Apex 中的表达式可以是:字面量表达式;新的 sObject/Apex 对象/List/Set/Map;L-值(变量、列表位置、字段引用);sObject 字段引用;方括号中的 SOQL/SOSL 查询;或静态/实例方法调用。

表达式概述

核心运算符

赋值运算符:=+=-=*=/=|=&=++--

算术运算符:+-*/

  • 加法规则:Integer + Integer = Integer;含 Double 则结果为 Double;Date + Integer = 新 Date;Datetime + Integer/Double = 新 Date;String + 任何非 null = 拼接

比较运算符:<><=>===!====!==

  • 与 Java 的关键区别:== 在 Apex 中比较的是对象值相等而非引用相等(用户定义类型除外)
  • String 比较使用 == 时不区分大小写,基于用户语言环境
  • ===!== 检查精确相等(相同的内存位置)

逻辑运算符(短路求值):&&(AND)、||(OR)、!(NOT)

三元运算符:x ? y : z —— 如果 x 为 true 则返回 y,否则返回 z

运算符

位运算符与特殊运算符

位运算符:&(AND)、|(OR)、^(XOR)、~(取反)、<<(左移)、>>(有符号右移)、>>>(无符号右移)

整数除法陷阱:

Double d = 5/3;     // 返回 1.0(非 1.666...)小数部分被截断!
Double d = 5.0/3.0; // 返回 1.6666666666666667 —— 保留小数
位运算符

安全导航运算符(Safe Navigation Operator)—— ?.

使用 ?. 替代显式的、顺序的 null 引用检查。当左侧表达式为 null 时短路并返回 null,而不是抛出 NullPointerException。

// 如果 a 为 null,返回 null;否则返回 a.b
a?.b;

// sObject 链式访问
String s = contact.Account?.BillingCity;

// SOQL 查询
String lastName = [SELECT LastName FROM Contact]?.LastName;

不允许使用的场景:带点号的类型/静态表达式(命名空间、Trigger.new、{Type}.class)、静态变量/方法调用(AClass.AStaticMethod())、可赋值表达式(foo?.bar = 42;)、SOQL 绑定表达式、sObject 标量字段的 addError()。

安全导航运算符

空值合并运算符(Null Coalescing Operator)—— ??

?? 运算符在其左侧操作数为 null 时返回右侧操作数。类似于 ?.,它替代了代码中冗长的 null 检查。

// 之前:
Integer notNullReturnValue = (anInteger != null) ? anInteger : 100;

// 使用 ??:
Integer notNullReturnValue = anInteger ?? 100;

特点:

  • 二元运算符 a ?? b:如果 a 不为 null 返回 a,否则返回 b
  • 左结合;左操作数只求值一次;右操作数仅在左操作数为 null 时求值
  • 类型必须兼容——两个操作数必须是同一类型的实例
空值合并运算符

空值合并运算符与 SOQL

使用 ?? 优雅地处理 SOQL 查询无结果的情况:

Account defaultAccount = new Account(name = 'Acme');
Account a = [SELECT Id FROM Account WHERE Id = '001000000FAKEID'] ?? defaultAccount;
注意运算符优先级:top ?? 100 - bottom ?? 0 被解析为 top ?? (100 - bottom ?? 0),而非 (top ?? 100) - (bottom ?? 0)。必要时使用括号明确意图。

限制:不能作为赋值的左侧(foo ?? bar = 42; 无效);SOQL 绑定表达式不支持;Salesforce 建议不要在单个语句中同时使用多个 SOQL 查询和 ?? 运算符。

空值合并与SOQL

运算符优先级

运算符按优先级规则解释,从高到低:

  1. {} () ++ --(分组、前缀增减)
  2. ~ ! -x +x (type) new(一元、类型转换、对象创建)
  3. * /(乘法除法)
  4. + -(加法减法)
  5. << >> >>>(移位)
  6. < > <= >= instanceof(比较、引用测试)
  7. == !=(相等比较)
  8. & ^ |(位运算 AND/XOR/OR)
  9. && ||(逻辑 AND/OR)
  10. ??(空值合并)
  11. ?:(三元)
  12. = += -= *= /= &= |= <<= >>= >>>=(赋值)
运算符优先级

注释

Apex 支持单行注释和多行注释:

// 这是单行注释
Integer i = 0;  // 行内注释

/*
  这是
  多行注释
*/

我们推荐使用标准化的 ApexDoc 注释格式以提高代码可读性和长期可维护性(基于 JavaDoc 标准,专为 Apex 和 Salesforce 生态系统定制)。

注释

赋值语句与类型转换

赋值与类型转换

赋值语句

赋值语句将值放入变量中。赋值始终按引用进行:

Account a = new Account();
Account b;
a.Name = 'Acme';
b = a;  // b 现在引用与 a 相同的对象
System.assertEquals(b.Name, 'Acme');  // true

除了 = 之外,其他有效的赋值运算符包括 +=*=/=|=&=++--

赋值语句

类型转换规则

Apex 一般要求你显式地将一种数据类型转换为另一种。数字类型存在层级结构(从低到高):

Integer → Long → Double → Decimal

较低数字类型的变量可以始终赋值给较高类型而无需显式转换。

隐式转换(自动,无需方法):

  • Integer → Long、Integer → Double、Long → Double
  • ID → String(始终允许)
  • String → ID(运行时检查——如果不是合法 ID 则抛出异常)
  • Date → Datetime(时间组件清零)

显式转换(需要方法):

  • String → Integer:Integer.valueOf(str)
  • String → Decimal:Decimal.valueOf(str)
  • Integer → String:String.valueOf(int)
  • 可使用 instanceof 关键字测试字符串是否为 ID
类型转换规则

数据类型的额外注意事项

  • 数值默认类型:数值默认为 Integer,Long 需要 L 后缀(如 2147483648L),Double/Decimal 需要 .0 后缀
  • 溢出与下溢:Apex 对数据类型值的溢出和下溢不抛出异常。例如 Integer i = 2147483647 + 1; 结果为 -2147483648(回绕到最小值)。为避免此问题,请在算术运算中使用足够大的类型并添加适当的字面量后缀
  • 整数除法丢失小数:Double d = 5/3; 返回 1.0 而非 1.666...因为在隐式转换为 Double 之前小数部分已被移除。使用 Double d = 5.0/3.0; 保留小数
  • Date 到 Datetime 转换:Apex 支持 Date 到 Datetime 的隐式和显式转换,时间组件清零

本章涵盖了编写 Apex 代码所需的所有语言基础。后续章节将深入探讨控制流语句和类的更多细节。