编写 Apex 代码:数据类型、集合、变量与运算符完全参考
Apex 语言核心完全参考:涵盖所有数据类型(原始类型、sObject、集合)、List/Set/Map 三种集合的完整用法与注意事项、多行字符串、枚举与系统枚举、变量声明与命名规则、Null 处理与作用域、常量、安全导航运算符(?.)与空值合并运算符(??)的完整说明、运算符优先级表、赋值语句与类型转换规则,以及溢出和除法精度等重要注意事项。
本课程共有 33 个章节
同学们,大家好,今天我们翻开 Apex 语言参考这一章。这章就像一本工具手册,会帮你把 Apex 代码的各个零件都梳理清楚。 我们从最基础的地方开始——数据类型。在 Apex 里,你写的每一个变量、每一个表达式,都有明确的类型。 比如,你可能会用 Integer 存整数,用 String 存文字,这些我们叫作基元类型。 如果你要操作 Salesforce 里的数据,就会碰到 sObject 类型,什么 Account、Contact、Opportunity,都属于这种。 还有专门用来装一组数据的集合,像 List、Set 和 Map,它们各有各的脾气,待会儿我们单独说。 此外,还有枚举和咱们自己定义的类,这些都是数据类型。 接下来,我们聊聊集合。集合主要就三种。 List 是有顺序的列表,你可以按位置去拿数据,最常用。 Set 是无序的,里面不能有重复值,适合拿来去重。 Map 是键值对,靠一个唯一的键去找到对应的值,就像用 Id 查 Account。 每种集合都有特定的创建写法,比如 new List<String>(),而且在不同场景下,你会选不同的集合来用。 说完集合,我们再看看变量。 变量起名有自己的规矩,一般用字母、数字和下划线,但不能用数字打头。 声明变量的时候,我们一般要给它一个明确的类型,也可以同时初始化,比如 Integer count = 0。 每个变量都有自己的活动范围,我们叫作用域,出了范围就访问不到了。 如果你想定义固定不变的值,可以用 final 定义常量。 然后就是公式和运算符。除了常见的加减乘除、大于小于这些,Apex 还有两个很贴心的特殊运算符。 一个是安全导航运算符,用 ?. 表示。打个比方,account?.Name,如果 account 是空的,表达式直接返回 null,不会给你扔一个空指针异常,就这样很安全。 另一个是空值合并运算符 ?? ,如果左边的值是 null,就让整个表达式取右边的默认值,特别适合给变量一个兜底的值。 这两个运算符一配上,代码平时就不容易崩。 最后,我们还要了解赋值和类型转换。 Apex 是强类型语言,很多时候你得亲自把一种类型转成另一种。 比如,把一个 Double 转成 Integer,你可以用 Integer 强制转换,或者利用一些自动转换规则,像小范围整数自动转成大范围。 要记住,转换不是随心所欲的,有它自己的规矩,弄不好就报错。 所以总结一下,这一章就像一张地图,帮你搞清楚数据类型、集合、变量、运算符和转换这些基本部件。把它们都弄熟了,后面写 Apex 代码才能游刃有余。 好了,这节课先到这里,后面我们会一个一个点深入去练。
来,咱们看看Apex里数据类型这一块。想象一下,你在填写表格,每个格子都有规定的填法,数据类型就是这种“规定”。每个变量、每个表达式,在Apex里都得有个明确的数据类型。 首先,有一些基本的类型,我们叫它们“基元”类型。就像搭积木最基础的那几块:你看有“大写”?不对,应该是布尔值(Boolean),就是真或假。还有整数(Integer)、长整数(Long)、带小数点的数字(Double)、日期(Date)、日期时间(Datetime)、一串文字(String),还有唯一标识(ID)。这些就是最常用的砖瓦。 然后,我们有sObject类型。这就像是一张张不同的表格:可以是一张通用的表,也可以特指“客户”(Account)、“联系人”(Contact),或者你自己建的“自定义对象”(像 MyCustomTarget__c 这样)。用它,你就能把整行数据当成一个整体来操作。 再说说集合,它可以帮你把一堆东西装起来。有三种主要容器: - 列表(List):有序的,可以放重复的东西,就像购物清单,按顺序排列。 - 集合(Set):无序的,而且里面的东西都是独一无二的,重复的自动被忽略,就像一袋子不重样的弹珠。 - 映射(Map):是键值对的组合,好比字典,你通过一个词(键)查到对应的解释(值)。 其他的类型还有枚举,它是一组固定的常量值;还有用户自己定义的Apex类、系统提供的各种类,以及一个特殊的空常量(null)。 方法(函数)可以返回任何上面提到的类型,如果它什么都不返回,我们就标记返回类型为 Void,也就是空的。 最后要记住最重要的一点:在Apex里,类型检查非常严格,在你保存代码编译的时候就会做。如果你不小心把一串文字(String)赋值给一个应该放数字(比如Integer)的地方,解析器马上会报错,不让你通过。这就像交通规则,不遵守就亮红灯,保证代码的健壮和清晰。 这样讲,你能想象出这些类型在代码里怎么互动了吗?都是些基础的“建筑材料”,组合起来才能盖出功能丰富的大厦。
同学们好,今天咱们来聊聊 Apex 的数据类型,这可是写代码的基础,一定要清清楚楚地把握好。 首先,Apex 用的基本数据类型,跟 SOAP API 里的那些原始类型是一样的,比如说整数、字符串、日期、布尔值这些,咱们平时 Web 服务交互都能看到它们。不过有个小特殊的地方,那就是小数类型——Apex 里用的是更高精度的 Decimal,算钱、算精确数值的时候,它比普通的 double 靠谱多了,这个要记一下。 另外一个特别重要的点:在 Apex 中,不管你定义的变量是类里面的成员变量,还是方法内部临时用的变量,,全部会自动初始化成 null,,也就是空的值。千万别以为数字会默认是 0,布尔会默认是 false,不是的,它们一开始都是什么都没有,是 null。 所以你在用变量之前,,一定要主动给它赋一个合适的初始值,。举个最常见的例子,布尔变量,如果你打算拿来当开关控制逻辑,记得先把它设成 false,比如 `Boolean isActive = false;`,不然直接拿一个 null 去判断,程序就容易出问题。 那么今天这一节,我们会完整地过一遍所有原始数据类型的集合,你看完后就知道 Apex 到底支持哪些基本类型,各自怎么用。还会专门讲一讲多行字符串的写法,以及随着 Salesforce 版本升级,某些数据类型的行为发生了一些变化,这些变化也可能影响到你已有的代码,需要留个心。 好,这些就是我们这一节要聊的内容,我们一个一个慢慢说。
今天我们来聊聊Apex中的原始数据类型,这就像是编程世界里的基础积木。先来看Blob,它是用来存放二进制数据的,比如文件附件,还支持跟字符串互相转换,非常灵活。布尔类型呢,只能存真、假或者空,顺手一提,用布尔变量前最好先把它设成假,免得踩坑。 Date类型只存日期,不带时间,比如生日啥的。Datetime就是日期加时间,适合记录事件的具体时刻。December这个类型有点特殊,它支持任意精度,处理金额的时候用它最靠谱。最后说到小数位数,如果是从数据库字段提取的,位数就跟着字段定义走;要是从字符串转过来,就看小数点后面有几位;从非小数类型转来的话,会先变成字符串再定,规则挺清晰的。 这些基础类型用熟以后,写代码就会顺手很多,下次我们继续深入实践。
好的同学们,今天我们来看看Apex里几种最常用的数据类型。别担心,我会用最简单的方式讲清楚。 首先,数字类型。我们常用的有几种:一个是Double,它是64位的浮点数,就是说它既能存整数,也能存带小数点的数。如果你需要一个很大的整数,比如记录条数或计数器,就用Long,它是64位的有符号整数。还有一个32位的有符号整数,你可能在文档里见过它叫Integer,有时候缩写会写成RST,但实际上在代码里我们直接写Integer就好。记住,32位和64位的区别主要在于能表示的数字范围大小。 接下来,有一种Apex里非常有特色的类型:ID。每个Salesforce记录都有一个唯一标识,比如账户、联系人,它们的ID字段就是这种类型。标准ID是18个字符,但你有时候会看到15个字符的版本。别担心,如果你把15个字符的值赋给一个ID变量,Apex会自动帮你补成18个字符。所以处理ID的时候,你几乎感觉不到这个转换。 还有一个特殊的类型叫Object。在Apex里,所有数据类型其实都继承自Object这个根类型。也就是说,你可以把一个整数、一个字符串、甚至一个自定义对象,都塞进一个Object类型的变量里。需要的时候,再把它强制转换回原来的类型。这就像一个大箱子,什么都能装,但要拿出来用的时候,你得知道它原来是什么东西。 再说字符串String,这个用得最多。写法很简单,用单引号把文本括起来,比如'Hello World'。如果你要写多行文本,比如一段长留言,就用三重单引号开头和结尾。像这样: ``` String msg = '''第一行 第二行 第三行'''; ``` Apex里的字符串有个和别的语言不太一样的地方:它可以是null(没有值),也可以是空字符串''。但Salesforce数据库里的字符串字段(比如Text字段)是不一样的,它们只能为空字符串,而不能是null。所以从数据库读取字符串时,我们一般不用操心null,但自己定义变量时就要注意了。 字符串还支持一些转义序列,就是在字符串里表示一些特殊字符。这些和SOQL查询语言里用的完全一样。比如:\n是换行,\t是制表符,\\表示一个反斜杠本身,\'是单引号(因为字符串外面也是单引号),还有\r、\b、\f等等。你用得最多的大概就是换行和制表符了。 最后,一个非常好用的特性:在Apex里,字符串可以直接用比较运算符来比较大小。什么意思呢?比如你可以写 if ('apple' < 'banana') ,这是合法的。比较的时候,它是按照当前用户的语言区域,做不区分大小写的比较。也就是说,'A'和'a'会被当成一样。这在很多其他语言里是做不到的,比如Java里字符串比较必须用equals方法。Apex的这个设计让代码写起来更自然,但也要注意它和严格的大小写敏感比较不一样。 好了,今天我们把数字、ID、Object、字符串这几个基础数据类型的重点都捋了一遍。大家先有个印象,写代码的时候慢慢体会,遇到问题再回来看看这些规则。
同学们,今天咱们来看Apex中一个很方便的写法——多行字符串。 平时我们写字符串,都是用一对单引号括起来,内容只能写在一行里,稍微长一点就得拼拼接接,很麻烦。Apex现在支持用,三个单引号,来包裹一段文本,这样你就可以直接在代码里写好几行的内容,非常直观。 这种多行字符串有几个重要的特性,我一个一个给你讲。 首先,它会把你在代码里写的标点符号、空格、缩进,基本原样保留。不过,Apex编译器会聪明地帮你清理掉多余的空白,具体规则是这样的:它会找出整个字符串里,最靠左的非空白字符,,然后以那个位置为基准,把每一行开头多余的空白都去掉;同时,每一行末尾的多余空格也会被自动删除。这样,即使你在代码里为了美观写了一大堆缩进,也不会影响字符串真正的内容,比如: ```apex String poem = ''' 春眠不觉晓, 处处闻啼鸟。 '''; System.debug(poem); ``` 实际字符串会变成“春眠不觉晓,\n处处闻啼鸟。”,前面那八个空格缩进全都被剥掉了,只保留行与行之间的换行。代码里的断行会自动变成新行序列,也就是咱们说的换行符。 另外,要注意这个空白剥离的时机:它是在,编译期间,发生的,而且排在处理反斜杠转义序列,之前,。换句话说,Apex先把多余的缩去掉,然后再去解释像`\n`、`\t`这类转义字符。所以你完全可以在多行字符串里继续使用转义符,比如`\n`表示换行,`\'`表示单引号,规则和普通字符串一样。 最后说一下单引号的使用。因为整个字符串是用三个单引号包起来的,所以里面的,普通单引号根本不需要转义,,你直接写就行,例如: ```apex '''He said, "It's a secret."''' ``` 这完全合法。但有一个小例外:如果你想让字符串,在末尾出现单引号,,而且它正好紧挨着结束的三个引号,那就必须转义,不然编译器会误以为字符串提前结束了。比如你要输出`Hello'`然后紧跟着结束,就得写成: ```apex '''Hello\'''' ``` 或者 ```apex '''Hello'\'''' ``` 简单来说,只要你的单引号不直接触碰到右边三个引号,就不用管它;触碰了,就加个反斜杠。 好,多行字符串就讲到这里。这个特性在处理长文本、SQL查询或者JSON模板的时候特别有用,以后你会经常见到它。有什么疑问随时提出来。
同学们,今天我们来看看Apex里面多行字符串的几个实用小细节。多行字符串,就是你可以让一段文本跨越多行,不用在一行里写得很长。很简单,用单引号或者双引号把它包起来,然后在里面直接换行就可以了。 先记住一个要点:多行字符串完全支持我们常用的那些转义序列。比如你想在字符串里加个换行符、制表符,直接用反斜杠加字母就行。 但有两个特殊的转义值得单独说一下。 第一个,如果你想在一行的末尾故意保留几个空格,正常情况下Apex会自动帮你清掉每一行末尾的空白。那怎么强行留住那些空格呢?你就在那一行的最后写上反斜杠s,也就是 `\s`。这样,尾巴上的空白就被保留下来了,不会被系统修剪掉。 第二个,有时候你写多行字符串是想换行排版,但实际字符串内容里并不想要换行。那怎么办?你可以在前一行的末尾打上一个反斜杠 `\`,注意,这个反斜杠单独出现,不是转义哪个字母。它的作用就是把两行连接起来,而且它自己不会变成换行符,也不会插入任何新东西。于是你虽然写了多行,得到的还是一个连贯的长字符串,只是代码看起来更整齐而已。 接下来聊聊多行字符串在查询里的用法。你写SOQL或者SOSL的时候,如果查询条件里需要一段多行文本,可别直接把多行字面量写在方括号里面,比如直接 `[SELECT ... WHERE Field = '第一行\n第二行']` 这样是不行的。与普通字符串字面量不同,多行字符串不能直接放在查询语句里当值来用,除非你用绑定表达式。 那什么叫绑定表达式呢?很简单,你先把那段多行内容存到一个变量里,比如叫 `myText`,然后在查询里用冒号加上这个变量名,像 `:myText` 这样。Apex就认了,它会从变量里取出多行字符串的值再执行查询。也就是说,多行字符串本身没问题,它只是不能直接“硬写”在查询的字面量里,通过变量绑定就是完全合法的。 除了SOQL和SOSL里的这个小限制,其他所有能用普通字符串的地方,你都可以直接使用多行字符串,没有任何区别。比如做字符串拼接、放在列表里、或者传给方法当参数,统统都可以。 所以啊,大家只要记住:用 `\s` 刻意留空白,用 `\` 来拼接去换行,写查询时记得先存成变量再用冒号绑定,多行字符串就随你怎么用都行。 好了,这个小知识点就讲到这里,大家动手试试就会很清楚了。
同学们,今天咱们来聊聊 Apex 里数据类型的一些版本变化,这个内容虽然小,但实际开发中特别容易踩坑。我尽量用大白话给你讲清楚。 首先,从 API 版本 16.0 开始,也就是比较新的版本里,Apex 在一些类型上用了更高精度的 Decimal 数据类型。比较典型的就是“货币”类型。以前可能精度不太够,现在计算金额的时候会更精确,这其实是个好事儿。你在代码里直接写金额数值或者从字段里取值,系统底层已经自动帮你用高精度了,一般不用额外操作,但心里得有数。 第二个变化是从 API 15.0 开始的。以前如果你把一个特别长的字符串塞进一个字段,可能系统就悄悄截断了,不告诉你。但现在不是了,如果你在 Apex 类或者触发器里,给一个变量赋的字符串值,长度超过了字段定义的长度,系统会直接抛出一个运行时错误。这就提醒咱们,写代码的时候要检查一下字段长度,尤其是拼接字符串、处理外部数据的时候,一不小心程序就崩了,所以这个变化其实是强制我们规范操作。 最后,再说两个特殊的数据类型,叫 AnyType 和 Currency。它们不是标准的原始类型,你不能声明一个变量类型是 AnyType 或者 Currency,也不能当方法参数用。那它们在哪儿出现呢?只在系统提供的静态方法里。比如 AnyType 这个类型,是用来做字段历史追踪的,我们查看某个字段修改的历史记录时,系统返回值就是 AnyType 类型,你拿到以后需要转换成实际的类型才能用。而 Currency 这个类型就更局限了,只用在 SOQL 或者 SOSL 查询的 WHERE 条件里,比如你想查某个金额字段等于某个值,就可以用 Currency 类型来表示精确的货币值。但你平时写代码的时候,直接用 Decimal 来表示钱就行了,不会去直接创建 Currency 对象。 总结一下,版本升级之后,数据类型更严格更精确了,咱们写代码时要留意字段长度限制,也记住 AnyType 和 Currency 这两个神奇的类型只能看不能用。这样你在实际开发里就能少碰些莫名其妙的报错。好了,这块内容就这些,有问题随时问我。
同学们,这节课我们来聊聊Apex里一个非常重要的概念——收藏,也就是集合。你可以把它理解成一个用来装数据的容器。Apex提供了三种很强大的收藏类型,分别是列表、集和地图。 首先看列表,List。它是一个有序的集合,就像你写购物清单一样,每个项目都有它的位置顺序,而且允许有重复的项。比如你可以往列表里放两个“苹果”,完全没问题。当你需要按顺序处理一组数据时,用列表就非常方便。 第二种是集,Set。它和列表不同,是无序的,而且里面的元素必须是唯一的,不能有重复。你可以把它想象成水果沙拉里的各种水果,苹果只能出现一次,如果尝试再放一个苹果进去,它就会自动忽略,保证每种水果只出现一次。集常用来做去重或者检查某个值是否存在。 第三种是地图,Map。地图存储的是键值对,也就是一个唯一的键对应一个值。它就像一个真正的字典,你想查一个词的解释,直接用这个词作为键就能快速找到对应的值。地图最适合需要高效率查找的场景。 这三种收藏类型都是使用带尖括号的参数化类型来指定元素类型的。听起来有点绕,其实很简单,就是在定义的时候,尖括号里写上你要存放的数据类型,比如List<String>就是一个存放字符串的列表,Map<Id, Contact>就是用记录ID做键、Contact记录做值的地图。这样做能保证类型安全,也能让代码更清晰。 最后,关于容量,从数量上讲,这些收藏里能放的项目数量并没有硬性上限。不过要注意,Apex整个运行环境有堆大小的限制,也就是总内存有上限。所以虽然你可以随意往里面加东西,但如果数据量太大,超出了系统分配给你的内存,程序就会报错。所以要合理使用,不要一次加载太多记录哦。 好,这就是关于收藏的基本介绍,接下来我们会详细演示每种类型的用法。
好,我们来看一下Salesforce里的列表,英文叫List。 列表其实就是一个有序的集合,你可以想象它像一排贴了号码的储物柜,每个位置都有一个编号,从零开始。比如第一个位置是0号,第二个是1号,以此类推。 列表里面的元素可以重复,比如你可以把同一个客户的名字放进列表里好几次,这完全没问题。 那怎么创建一个列表呢?很简单,用List这个关键字,后面跟上一对尖括号,括号里放元素类型。比如你要一个存字符串的列表,就写List<String>。如果你要存数字,就写List<Integer>。 你可以直接在定义的时候给列表填好初始值,用大括号括起来就行了。比如List<String> colors = new List<String>{'红', '绿', '蓝'}; 这样就创建了一个有三个颜色的列表。 还有一点很有趣,列表本身可以装其他集合,甚至能相互嵌套。也就是说,一个列表的元素,本身又是一个列表,你一直可以往里套娃。不过Apex限制了嵌套的层数,最多能套到七层嵌套集合,加上最外层,总共最多八层。超过这个深度就不行了,所以你记得别套太深就好。 总结一下,你今天只需要记住,列表有序、有索引从0开始,可以重复,用List<类型>来声明,可以大括号初始化,还能嵌套但别超过八层。这些就是列表的基础啦。
同学们,咱们今天来聊聊Apex里最常用也最有趣的一个东西——列表,也就是List。你可能会在代码里看到两种写法,一种像数组,一种像列表,其实它们很多时候是一回事。但Apex的列表可比传统数组灵活得多。 咱们先看声明和引用。如果你定义一个一维列表,里面装的是基本类型,比如String,或者装的是自定义对象,完全可以像老式数组那样写:String[] colors = new String[1]; 这就创建了一个固定大小为1的字符串列表。引用的时候,用colors[0]拿第一个元素,这没问题。 但Apex的列表天生有弹性。什么意思呢?哪怕你一开始声明它只有1个位置,后期想让它变大,直接用列表的add方法就行了。比如colors.add('Blue'),它会自动扩张,不知不觉装下更多东西。这就是列表的弹性——add方法能动态添加元素,不用管原来声明了什么大小。 不过这儿有个关键点要记住:如果用方括号去添加元素,比如colors[1] = 'Red',那列表就变得像数组一样死板,没有弹性了。如果你这么写,但索引位置1不存在,系统会直接报错,说列表索引越界。所以,用方括号只能修改已有位置的值,不能添加新位置,。想灵活添加,一定用add方法。 好,再说排序。列表自带的sort方法特别方便,调用一下,不用写复杂算法,就能按升序给你排好。对于基本类型,比如数字、字符串,sort直接搞定。对于自定义类型呢?你要么让那个类实现Comparable接口,让对象自己知道怎么比大小;要么在sort时传入一个Comparator,用自定义的比较逻辑。这样不管多复杂的数据,都能轻松排序。 还有一个稍微特殊的情况:你在SOQL里用SelectTerm,会返回一个列表,里面每项都有value和label两个字段。这种列表调用sort时,排序规则是:先按value排序,如果value相等,再按label排序。这个细节在做下拉菜单排序的时候特别有用,要记住。 总结一下,Apex列表融合了数组的直观和List的动态弹性。声明时可以用数组方括号,但要动态增长,就用add;别用方括号越界赋值。排序用sort,自定义对象要么实现Comparable,要么传Comparator。记住这些小技巧,你的代码会清爽又安全。下节课咱们继续深入,看看更复杂的集合操作。
同学们,集合,在Apex里是一个很常用、也很重要的数据类型,我们花几分钟把它彻底搞清楚。 先说定义:集合就是一组元素,但有两个核心特征——,唯一,和,无序,。 唯一,意味着里面不能有重复的东西。如果你把一个已经存在的元素再加进去,Apex不会报错,而是悄悄忽略掉,就像什么都没发生一样。这一点和列表完全不同,列表是允许重复的。 无序,是说集合没有“第一个位置、第二个位置”这种索引概念,你不能像操作列表那样用下标去拿元素。所以,它不适合需要按顺序访问的场景。 那么底层是怎么实现的呢?有意思的是,在Apex里你完全不需要操心这些——内部统一用的是哈希结构。和Java不同,不用我们去选择到底是HashSet还是TreeSet,Apex帮我们做了这个决定。你只管用就好了。 不过要记住一个特别容易被误解的点:虽然叫“无序”,但,迭代顺序是确定的,。也就是说,如果你用for循环遍历同一个集合,只要里面的数据没变,每次运行遍历出来的顺序都是一样的。所以你可以放心地依赖这个顺序,它不是随机的。 最后,集合在Apex中最经典、最常见的用法,就是收集要在,SOQL的WHERE IN子句,里使用的那批唯一的记录ID。比如你从好几个地方拿到一堆ID,里面可能有重复的,你先把它们往集合里一塞,重复的自动就去重了,然后再拿这个集合直接写 `[SELECT ... FROM ... WHERE Id IN :mySet]`,干净又高效。这个场景下,集合就是天生的利器。 这样讲,是不是清晰多了?好,我们接着看下一个知识点。
同学们,咱们今天来看一个特别实用的知识点——Apex里的Map,也就是映射。 你可以把Map理解成一个存放“键值对”的容器,就像一本通讯录。通讯录里,每个联系人有一个独一无二的姓名作为“键”,后面的电话号码就是“值”。这个键必须是唯一的,一个姓名只能对应一个最新号码,如果你又用同一个姓名存了一个新号码,那旧号码就被覆盖掉了。而值是可以重复的,不同联系人当然可以有相同的电话号码。 在Apex里,我们怎么初始化一个Map呢?用大括号括起来,然后用一个箭头 `=>` 把键和值连起来。比如这样: ```apex Map<String, String> phoneBook = new Map<String, String>{ '张三' => '13800001111', '李四' => '13900002222' }; ``` 你看,是不是很直观? 在Apex编程里,有一个最常用的Map模式,就是从记录ID到sObject对象的映射。因为Salesforce里的每一条记录,无论是客户、联系人或自定义对象,都有一个全局唯一的ID,用这个ID作为键,可以直接拿到对应的整个记录对象,查找起来非常快,是恒定时间,也就是说,无论数据量多大,这个查找几乎都瞬间完成。 所以,我们经常能看到类似 `Map<Id, Account>` 这样的声明。比如你有一个账户列表,你想通过ID快速找到某个账户,这时Map就太好用了。 而且,Apex还贴心地提供了一个非常方便的构造函数,你只要把一个sObject列表直接传进去,它就会自动帮你生成一个以每条记录的ID为键、记录本身为值的Map。就好比把一堆名片扔进一个神奇机器,瞬间就整理出一本按编号索引的名片簿。比如: ```apex List<Account> accList = [SELECT Id, Name FROM Account LIMIT 10]; Map<Id, Account> accMap = new Map<Id, Account>(accList); ``` 这样,你就可以随时用 `accMap.get(someId)` 拿到对应账户了。 好,这个Slide的核心就这么多。简单回顾下:Map是键值对,键唯一值可重复;用箭头语法初始化;最常见的是Id到sObject的Map,实现快速查找;并且可以直接用列表构造出这个Map。在后面的触发器、批量处理里,你会经常跟它打交道,把它掌握好,你的代码效率会高很多。
同学们,今天我们来聊聊Apex里非常实用的一个数据结构——Map,也就是映射,或者叫字典。 如果你有Java基础,你会发现一个不一样的地方。在Java里,Map有不同的实现,比如HashMap、TreeMap,但Apex简化了这一切。Apex所有Map底层用的都是哈希结构,你不用自己去选择算法。这样做的好处是,你只要会用Map就行,不用关心底层细节。 还有一个有意思的特点:Apex里Map的迭代顺序是确定的,也就是说,你遍历键值对的时候,每次出来的顺序都是一致的。这一点和某些语言里无序的HashMap不太一样,所以你可以依赖于这个顺序。 再来说说键,也就是Key。如果键的类型是字符串,要注意它是区分大小写的。比如,"Name"和"name"会被当作两个完全不同的键。你存了两个值,它们互不影响。 那如果是你自己定义的类型当作键呢?这时候,唯一性是由你自定义类的equals和hashCode方法决定的。你重写这两个方法的时候,一定要小心,保证逻辑一致。不然可能出现明明同一个对象,却映射到不同的值的情况。 特别要提醒的是,用sObject做键的时候要谨慎。为什么?因为sObject的内容可能会变。比如你拿一个Account记录做键,存了个值。之后这个Account对象某字段被修改了,它就变成另一个状态了,可能再也找不回原来映射的那个值。所以,一般不推荐用sObject直接做键。 最后,关于序列化。Map对象可以序列化成JSON,但有个前提——只能用特定的键类型,比如字符串。如果键是数字或者日期等,直接序列化可能会有问题,这点开发中要注意。 那么关于Map的这些要点,今天就讲到这里,大家理解了吗?下节课我们会用例子演示这些特性。
同学们,咱们今天来看Apex语言里一个非常核心的特性,就是它的类型系统。 首先,Apex是一种静态类型的语言。什么意思呢?就是说,你在用任何一个变量之前,必须明确告诉系统这个变量是什么数据类型。比如你写个 `String name = '张三';` 或者 `Integer count = 10;` ,如果不声明类型,代码就会报错。这和那些动态语言不一样,它更严格,但也更安全,能帮你在写代码的时候就抓到很多类型错误。 接下来,咱们说说集合。列表、映射和集合这三兄弟,在Apex里都是“参数化”的。你可以把它们想象成一个容器,容器里装什么东西,你得提前定好。比如,List后面必须跟一个尖括号,里面写上具体的数据类型,像 `List<String>` 表示这个列表里只能放字符串,`List<Integer>` 放整数。你必须用真实的数据类型去替换那个占位符,构造的时候就得写清楚。 那现在有一个比较特别的地方。在Apex里,如果类型T是U的子类型,那么 `List<T>` 就会自动成为 `List<U>` 的子类型。举个例子:假设咱们有一个父类叫“动物”,还有一个子类叫“狗”,狗是动物的子类型。这时候,`List<狗>` 就可以被当作 `List<动物>` 来用。换句话说,你把一篮子狗,当作一篮子动物来处理,这在Apex里是合法、允许的。这个特性叫“协变”,它让代码更灵活,你可以写出更通用的方法来处理各种子类型的列表。 简单总结一下:Apex是静态类型,集合必须指定装什么类型,而且因为子类型的关系,装子类的列表可以直接赋值给装父类的列表变量。这几个点你掌握清楚了,以后写Apex代码的时候,类型转换就不会迷路了。 好,这节课的小知识点就讲到这里,咱们下回继续。
同学们,我们来看今天的一个小知识点——枚举,Enum。 枚举是什么呢?简单说,它就是定义一组固定的名字,代表几个互斥的选项。比如一年四季,春夏秋冬,不会有第五个;或者一副扑克的花色,红桃、黑桃、梅花、方块,就这么几种。 在Apex里,我们用 enum 关键字来声明。一旦你定义了一个枚举,它其实就变成了一个新的数据类型,跟Integer、String一样,你可以用它来声明变量、作为方法参数等等。 需要注意一点,和我们熟悉的Java不同,Apex的枚举本身没有构造方法的语法,你不需要也不能在里面写构造函数。 另外,在Apex里,你还可以把一个类直接定义成枚举,不过这时候有一个规则:记得不要写上 class 关键字,直接用 enum 开头就行。 好了,枚举的概念比较简单,大家记住它是一个有限选择集合的数据类型,在需要限定值的范围时非常有用。下一节我们继续深入。
好,我们来看看Apex里系统已经给我们定义好的一些枚举类型。这些枚举都很有用,我们一个一个来认识。 首先是 System.StatusCode,它对应的就是API调用时返回的错误代码。比如我们调用外部接口,或者在Apex里处理异常,经常能看到这个StatusCode,像“INVALID_SESSION_ID”这一类的,就是它。 然后是 System.LoggingLevel,这个你一定用过,它和 System.debug 一起用。比如 System.debug('某个信息', LoggingLevel.ERROR),这样控制日志的级别是错误、警告还是调试信息。 System.RoundingMode 用来指定数学运算时的取舍方式,比如除法结果是要向上取整、向下取整,还是四舍五入。 System.SoapType 和 System.DisplayType,这两个通常是在描述字段结果的方法里返回,比如调用字段的描述信息,告诉你这个字段在SOAP里是什么类型,在前端页面上展示成什么样子,像文本、日期、下拉列表等等。 System.JSONToken 呢,在我们手动解析JSON的时候会用到。它代表了JSON内容中的一个个标记,比如是开始一个对象、开始一个数组,还是一个字段名。 还有 ApexPages.Severity,这个专门给Visualforce页面消息用的,决定消息的严重程度,像确认、错误、警告、信息。 最后是 XmlNodeType,处理XML的DOM文档时,节点类型就用它,判断当前是元素节点、文本节点还是属性节点。 这些系统枚举,都带有一些常用的方法,比如 values 方法可以列出枚举里的所有值,name 方法能得到枚举值的字符串名称。但要注意,我们自己不能给这些枚举添加额外的方法,它们都是固定的,我们不能扩展。 简单来说,这些枚举就是Salesforce平台帮我们把一些常用的常量集合定义好了,直接用就行,省得我们自己去定义一堆静态常量。记住这些,写代码时能方便很多。
好,我们来看一下Apex里局部变量是怎么声明的。其实你只要写过一点Java,就会发现写法几乎一模一样,Apex沿用了Java风格的语法。比如你可以在一行里声明一个整数变量,甚至用逗号隔开,同时声明和初始化好几个变量,非常灵活。 但命名变量的时候,有一些规则一定要记住,不然代码会报错。我一条一条跟你顺一下,就像聊天一样,你不用死记硬背。 首先,变量名是不区分大小写的。这意味着`myVar`和`myvar`在Apex看来是同一个名字,所以你起名的时候要注意别搞混了。 然后,名字里只能包含字母、数字和下划线,不能有空格或其他特殊符号。 它必须以字母开头,不能以数字开头,这个和大多数语言一样。 还有几个关于下划线的小禁忌:名字不能以下划线结束,也不能出现连续两个下划线,像`my__var`这种是不允许的。 另外,不能用Apex的保留关键字当变量名,比如你不能叫`list`、`if`或者`class`,这些词是系统占用的。 长度上也有上限,变量名最长可以到255个字符,这其实已经非常宽裕了,一般你也不会起那么长的名字。 还有一个很实用的建议:虽然技术上行得通,但Salesforce不推荐让变量名和它所在的类名或者类里的方法名完全一样。这样写虽然不直接报错,但会让代码变得难读,容易产生误会,维护的时候特别容易踩坑。 总结一下,就是使用Java式的声明,命名时注意字母、数字、下划线的组合规则,避开保留字和同名的情况,你的变量声明就不会出问题。好,关于这个知识点我们就说到这里。
好,我们来看下一个知识点——变量初始化与空值。 在Apex里,你声明一个变量但没给它赋值,那它的值就是null,也就是空。空的意思很直白,就是这个变量现在没有指向任何实际的东西。 而且,不光是默认空着,你也可以主动把null赋给任何用基本类型声明的变量,比如整数、字符串,或者布尔型。 这里有个特别需要注意的点:如果你的变量是空的,你还去调用它身上自带的实例方法,那Apex直接就会报错,抛出一个叫NullPointerException的空指针异常。这在调试的时候经常碰到。 还有一个非常容易掉进去的坑,很多初学者以为布尔变量如果没有赋值,应该默认为false——这个想法是不对的。在Apex里,布尔变量跟其他所有变量一样,只要没显式赋值,它就是null,不是true,也不是false。一旦你拿一个null的布尔值去作条件判断,就可能触发空指针异常。 另外,关于变量的作用范围,你在一个代码块里的任意位置都可以定义变量,但要注意,它从定义的那一行开始,才占据作用域,后面的代码才能用,前面的代码里是看不到它的。 简单总结:声明不初始化就是空,空可以手动赋值,空的实例方法调用会崩,布尔没赋初始值不是false而是空,作用域从定义处开始。这些点记牢,写代码就会少踩很多坑。
同学们,我们来看一下Apex里的常数这个概念。简单说,常数就是一个变量,它的值一旦设定好之后,就再也不会变了。在代码里,我们会用 `final` 这个关键字来定义常数。`final` 的意思就是“最终”,它告诉系统:嘿,这个变量最多只能被赋值一次。你可以在声明它的那一行,直接把值给它;如果这个常数是定义在类里面,那也可以在静态初始化块里,通过调用一些静态方法来计算出一个值再赋给它。举个例子,第一个常数在声明的时候就直接初始化了,第二个呢,可能在静态块里通过一个静态方法的返回值来赋值。这样,就算初始化的逻辑稍微复杂一点,也能确保常数只被赋值一次。想要更详细的内容,你可以去查阅指南里关于 `final` 关键字的部分。好,这就是常数的基本用法,大家理解了吗?
好,同学们,我们开始讲一讲表达式和运算符。 先说说什么是表达式。你可以把它理解成一段能够算出具体结果的代码片段。它通常由变量、运算符和一些方法调用组成,不管里面怎么写,最终都会计算成一个单一的值。比如我们写 `3 + age`,或者`contact.Name.toUpperCase()`,这些都是表达式,它们最后就给你一个结果。 那为了构建这些表达式,Apex 给我们提供了一整套丰富的运算符。我把它们归纳成几大类,咱们一类一类看。 第一类,算术运算符。就是咱们最熟悉的加、减、乘、除、取余数,符号和数学里几乎一样,用来做数学计算。 第二类,比较运算符。用来比较两个值的大小或者是否相等,结果要么是真,要么是假。比如等于 `==`、大于 `>`、不等于 `!=`,这种常常写在 `if` 语句的判断里。 第三类,逻辑运算符。与逻辑、或逻辑、非逻辑,对应符号是 `&&`、`||` 和 `!`。它们可以把多个条件串起来,让判断更灵活。 第四类,赋值运算符。最基本的就是等号 `=`,把右边的值交给左边的变量。还有一些复合的,像 `+=`、`-=`,可以一边运算一边赋值,写起来更简洁。 除了上面这些基础运算符,Apex 还提供了两个非常独特又好用的操作符,专门帮我们优雅地处理 `null` 值,避免程序动不动就报空指针异常。你在别的语言里不常见到。 第一个是安全导航运算符,写法是问号加一个点 `?.`。当你用 `account?.Name` 这种写法时,如果 `account` 对象是空的,它不会崩,而是直接返回 `null`。省去了你写一大堆 `if` 判断 null 的步骤。 第二个是空值合并运算符,写法是两个问号 `??`。咱们有些资料里也叫它 NPS 聚结操作员,说的就是它。用法是:左边可能是个空值,右边是备用的值。如果左边有值就用左边,否则自动取右边的默认值。比如 `String name = inputName ?? '无名' ;` 这样处理缺省值非常干净。 好了,表达式和运算符整体就先介绍到这里。接下来我们一个个展开,看看具体怎么用,以及有哪些需要注意的小细节。
同学们,我们来看一下Apex里“表达式”这个概念。 简单说,表达式就是由变量、运算符和方法调用组合起来的东西,它算到最后一定只得到一个值。就像你们写数学算式,3+5 算出来就是 8,整个 3+5 就是一个表达式。 在Apex里,你写的表达式只能是下面这几种情况之一,我们一条一条来理一下: 第一种,文本表达,也就是字面量。比如直接写个数字 10,字符串 ‘Hello’,或者布尔值 true。 第二种,用 new 关键字来创建新的 sObject、Apex 对象、列表、集合或映射。比如 new Account()、new List<String>(),这也是表达式,它求值就得到一个新建的对象。 第三种,任何“左值”。左值听起来有点绕,其实就是可以放在赋值号左边的东西,比如变量名、一维列表中的某个位置、或者 sObject、Apex 对象的字段引用。像 myVar、list[0]、acc.Name 这些,都是表达式,它们能取出一个值。 第四种,sObject 的字段引用,注意这是上面左值的具体体现。比如你有一个存放联系人的列表,contacts[0].Id 就是引用列表中第一个联系人的 Id 字段。还有和 sObject 关联的子记录,像 acc.Contacts 这种关系字段,也算表达式。 第五种,方括号里的 SOQL 或 SOSL 查询,它们是实时求值的。像 [SELECT Id, Name FROM Account LIMIT 1] 这条查询,本身就是一个表达式,会返回一条记录或一个列表。 最后一种,静态方法或者实例方法的调用。比如 Math.abs(-5) ,或者是 System.debug(),只要方法有返回值,这个调用整体也算表达式,求值得到方法的返回值。 记住,当你写代码时,任何能算出一个值的小结构,别管多复杂,它都必须是上面这些形式中的一种。这样在写条件判断、赋值语句的时候,你就能想清楚这里到底能用什么。 怎么样,这样梳理下来,是不是清晰多了?以后遇到复杂的表达式,就把它拆开看,肯定跑不出这几种情况。
好,我们来看看Apex里的运算符,也就是用来做计算和比较的那些符号。先讲算术运算符,这个大家应该很熟悉,就是加减乘除,分别用加号、减号、星号、斜杠表示。不过Apex多了一个求幂的运算符,用两个星号来表示,比如2的3次方,你就写2 , 3,结果是8。 接下来是比较运算符。除了常见的大于、小于、等于这些,Apex还多了两个严格相等的符号:三个等号和三个等号前面加个感叹号。这三个等号不光比较值,还会检查数据类型一不一样。比如数字1和字符串"1",用两个等号比较可能给你返回true,但用三个等号就会返回false,因为类型不同。 说到等于,这里要特别注意,在Apex里,用两个等号比较的是对象的值,而不是它们在内存里的地址,这一点跟Java不一样。不过,如果你比较的是自己定义的类的对象,那它还是会去比较引用。另外,字符串的比较是不区分大小写的,而且会根据当前用户的区域设置来处理,比如在某些语言里,带重音的字母可能会被认为跟不带重音的一样。 逻辑运算符用来组合条件。与运算用两个&符号,或运算用两个竖线,它们的计算方式是短路评估,也就是说如果左边的表达式已经能决定整个结果,右边就不会去算了,这可以提高效率。 最后还有一个三目运算符,问号加冒号,它是一种写简单条件判断的快捷方式。比如你有一个条件,如果为真就取A值,否则取B值,用三目运算符写出来就是:条件 ? A值 : B值;这样一行就搞定了,很简洁。 理解这些运算符对写Apex代码非常重要,多练习几次就熟悉了。
同学们,今天我们来看一下Apex里一些容易忽略,但又很实用的运算符细节。我会尽量讲得简单些,你一听就懂。 首先,Apex支持,按位运算符,。这些符号你可能见过:与(&)、或(竖线)、异或(^)、取反(~),还有位移运算符。它们直接对二进制位进行操作,比如权限控制、标志位处理时会用到。不过日常开发用得不多,咱们先有个印象,用到再查就行。 接下来,,加法运算符(+), 有一些特殊规则,这个要记住。 如果两个都是特定的资源类型(这里的RST你可以理解成一种特殊数值类型),那么RST + RST仍然得到RST。 如果是Date(日期)加上一个RST(整数),结果是新的Date,它会在原日期上加多少天。 如果是Datetime(日期时间)加上一个RST或者Double(小数),它会加上带小数的天数,比如加1.5天,就是一天半后的那个时间点。 还有字符串连接,只要你用一个字符串和任何非空类型相加,就会自动拼成字符串。就像 "Hello " + 123 会变成 "Hello 123"。 最后,,除法有一个重要的警告,,这个很多新手会掉坑里。 如果你用两个RST(整数类型)相除,Apex会先把结果的小数部分去掉,再隐式转换为Double。 比如,5 / 3,正常数学结果是1.666...,但Apex的处理是:先做整数除法,得到整数部分1(小数部分直接截掉,相当于向零取整),然后转成Double,所以最终结果是1.0,而不是1.666...。 如果你想保留小数,只要把其中一个操作数写成Double就行,比如 5.0 / 3 或者 5 / 3.0,结果就是1.6666666666666667。 所以,当你希望得到精确小数时,一定要记得用小数去参与除法。 好了,这几个要点记住了,写代码时就能避开不少小坑。下节课我们再讲别的。
今天我们来说一个很实用的小知识点——Apex里的,安全导航操作符,,也就是那个问号加点(`?.`)。 想象一下,你写了一串很长的对象引用,比如:`account.ContactPerson.Name`,但如果中间任何一个环节是空的,比如`ContactPerson`是null,代码就会直接崩掉,抛出一个大家都很熟悉的 ,NullPointerException,。 为了避免这个问题,我们以前的做法是写一堆`if`判断,挨个儿检查中间是不是null。这样代码就会变得又长又难看。 现在好了,Apex给我们提供了一个更优雅的写法,就是使用 `?.` 这个操作符。 它的逻辑很简单:,当你在链式调用方法、访问变量或属性时,只要在点号前加上一个问号,如果左边的那一部分计算出来是null,整个表达式就会直接短路,马上返回null,而不会继续往下执行,这样也就不会抛空指针异常了。, 举个例子: ```apex String street = account?.ContactPerson?.Address?.Street; ``` 如果`account`本身是null,或者`ContactPerson`为空,那么`street`就会被直接赋值为null,整个过程平平安安。 不过要注意,,安全导航操作符不是哪儿都能用的,。有些场景下就不能用它,你得记住: - 不能用在带点的类型或静态表达上,比如`ClassName?.StaticMethod()`这种不可以。 - 静态变量的访问或静态方法调用不可以。 - 不能用在赋值表达式的左边,因为它本身设计出来是读值用的。 - SOQL的绑定表达里不能用。 - 还有个特别的地方——,不能在`SObject`的纯量字段上链式调用`addLock()`,,这一点官方特别提了,要留意一下。 总的来说,只要你理解了它的短路保护机制,并且避开那些不能用的情况,它就能大大简化你检查空值的代码,让你写出来的东西既清晰又安全。 好,今天这一小节就到这里,我们下次继续。
同学们,今天我们来聊一个非常实用的小知识点——空合并运算符。别被名字吓到,它就是两个问号,写出来就是 `??`。这个运算符的作用很简单:如果它左边的值是空,那么就用右边的值来代替;左边有值,就直接用左边的值。 以前我们要做空值判断,代码很啰嗦。比如我们想给一个变量赋一个默认值,可能会写: `String result = (someVar != null) ? someVar : '默认值';` 或者更传统的 `if` 判断。现在有了 `??`,就可以直接写成: `String result = someVar ?? '默认值';` 是不是清爽多了? 你可能会觉得它和我们已经学过的安全导航操作符 `?.` 有点像。`?.` 是帮你安全地访问属性或方法,避免在对象为空的时候爆出空指针异常。而 `??` 是帮你给一个可能为空的东西立刻指派一个默认值。它们经常可以搭配使用,比如: `String displayName = account?.Name ?? '无名账户';` 如果 `account` 是空的,`account?.Name` 会直接返回空,然后 `??` 发现左侧是空,就把 `'无名账户'` 交给你。如果 `account` 有值,但 `Name` 属性就是 `null`,同样也能兜底。这就把两层保护写在一行里了,非常灵活。 使用 `??` 的时候,有几个小特点要留意。 第一,它是左结合的。也就是说,如果出现一连串的 `??`,比如 `a ?? b ?? c`,实际上会先算 `a ?? b`,如果这个结果还不是空,那就用这个结果;如果还是空,再去计算右边的 `c`。其实就相当于 `(a ?? b) ?? c`。 第二,左边的表达式只会计算一次,不用担心性能问题。而且右边的表达式是“惰性”求值的:只有当左边真的是空时,右边才会被计算。这样,如果右边的值涉及到消耗资源的方法调用,也没关系,只有需要的时候才会触发。 第三,一定要保证左右两边的类型是兼容的。你不能前面是一个 `Integer`,后面给一个 `String`,那样会报错。通常我们会对相同类型的变量使用,或者给一个能匹配的默认值,比如 `String` 配 `String`,`Decimal` 配 `Decimal`。 这样一来,原本好几行的空值检查,一行就解决了,代码的可读性和维护性都提上来了。你们在写触发器、控制器或者服务类的时候,遇到需要给变量设置默认值的场景,就大胆地把 `??` 用起来吧。
同学们,今天咱们来聊聊Apex里一个非常实用的小技巧——怎么优雅地处理查询不返回数据的情况。 想象一下,你写了个SOQL,想直接拿到一条记录,比如查某个客户的联系方式。代码可能长这样:`Contact c = [SELECT Name FROM Contact WHERE Email = 'xxx'];` 如果数据库里正好有这条记录,那没问题;但要是没有呢?程序就会抛出一个异常,啪一下中断了,你得额外写一堆try-catch来处理,挺麻烦的。 这时候,Apex提供的“空合并操作符”就能帮大忙。它长得像两个问号:`??`。怎么用呢?你把SOQL查询放在左边,右边放一个备用值。这么写:`Contact c = [SELECT Name FROM Contact WHERE Email = 'xxx'] ?? new Contact();` 意思就是,如果查询真的返回了一行,c就装那条记录;如果一行都没返回,就不会报错了,而是把右边这个新建的Contact对象赋给c。很聪明吧,一行代码就处理好了“可能没数据”的情况。 不过用起来有几点要留心。第一,这个`??`操作符不能放在赋值号的左边,它只能用来产生一个值,不能用来决定把某个值赋给什么。第二,SOQL语句内部是不支持直接写`??`的,它只作用在整个查询结果上。第三点也很重要,Salesforce建议咱们在一条声明语句里,不要混用多个SOQL查询再加上空合并操作符,不然代码读起来会乱,也容易出问题。 总结一下,当你需要做单条记录的查询,又怕查不到就报错,就用`SOQL查询 ?? 默认对象`这种写法,既简洁又安全。记住它的限制,就能让你的代码更健壮,不用再满屏的异常捕获了。 好,这一小块知识点就讲到这里,大家动手试试看吧。
接下来我们聊聊操作符的优先级。这个概念其实很简单,就是当一行代码里出现好几个操作符的时候,Apex会按什么顺序来执行它们。 你可以把优先级想象成排队,谁优先级高,谁就先被处理。 我们来看看顺序。最高优先级的,是括号和前置的自增自减,比如 `++x` 或 `--y`,这些永远最先算。 然后是那些一元操作符——就是一个操作数的,比如逻辑非 `!`、按位取反,还有类型转换 `(String)`,以及用 `new` 创建对象,它们也排得很靠前。 接着是乘法、除法和取模——乘除运算优先于加减运算,这和我们在学校里学的数学规则一样。 之后是移位操作符,比如 `<<` 和 `>>`,它们排在比较操作符前面。比较操作符呢,比如大于、小于这些,又排在相等性测试前面,也就是 `==` 和 `!=`。 再往后走,是按位与和按位或这类操作,然后是逻辑与 `&&` 和逻辑或 `||`。 还有一个要注意的,空合并操作符 `??` 的优先级比三元操作符 `?:` 要高,这两个别搞混了。 最后,最低优先级的,就是赋值操作符,像 `=`、`+=` 这些,总是最后才执行。 那这么多顺序万一记不住怎么办?很简单,当你觉得不确定的时候,直接用括号来明确你想先算哪一部分,括号里的永远最先执行,而且代码也更清晰易读。 就像以前老师说的,有疑问就加括号,准没错。
好,同学们,咱们今天来看一个挺实用的小知识点——Apex 代码里的注释怎么写。别看注释好像很简单,写好它可是能让代码更容易读、更好维护,团队合作也能省不少事。 首先,Apex 支持两种基础的注释方式。想快速写一行注释,你就在那一行开头打两个斜杠,也就是 `//`。编译器看到 `//` 之后,同一行右边所有的内容它都会直接忽略,不会当成代码去执行。这特别适合你临时记个思路,或者解释下面一行代码是干嘛用的。 那要是你需要写好几行的说明,用一行一行地敲 `//` 有点麻烦,这时候就可以用多行注释。你用一对 `/*` 和 `*/` 把要注释的内容包起来,中间想写多少行都行。比如: ``` /* 这段逻辑是计算客户折扣, 我们先检查等级, 再套用对应的折扣率 */ ``` 这样很方便吧。 不过呢,在实际项目中,我们更推荐一种叫 ,ApexDoc, 的标准注释格式。它其实是借鉴了 Java 世界里已经很成熟的 JavaDoc,专门为咱们 Apex 和 Salesforce 生态定制的规范。说白了,ApexDoc 就是用 `/, ... */` 这种形式来写注释,而且里面可以带一些特定的标签,像 `@description`、`@param`、`@return` 等等。举个例子,你写一个计算折扣的方法,上面可以这样注释: ``` /, * @description 根据客户等级和原价计算最终折扣金额 * @param level 客户等级,比如'Gold'或'Silver' * @param originalPrice 商品原价 * @return 返回折后价 */ public Decimal calculateDiscount(String level, Decimal originalPrice) { // 方法体... } ``` 这样做的好处可多了:第一,格式统一,别人一看就懂;第二,许多工具能直接根据这些注释自动生成代码文档,今后你维护起来方便;第三,长远来看,就算换人接手,他也能快速理解每个方法和类是做什么的,大大提高协作效率。 所以,同学们,以后写注释的时候,简单的临时说明你用 `//` 或 `/* */` 完全没问题,但对类、方法这些关键部分,一定记得套上 ApexDoc 格式,养成好习惯。这样你的代码会更专业,也更友好。 好了,这块内容就说到这儿,大家可以在自己的开发环境里试一下,有什么问题随时提。
讲完前面那些比较核心的规则,现在我们把一些同样重要,但更像“细节功夫”的点,一块儿梳理清楚。 这就像盖房子,大梁和承重墙都好了,现在是检查门窗、线路的时候。我们来看看,分配陈述、注释、类型转换和数字的一些注意事项,。 --- ### 一、分配陈述 这个其实最简单,你之前一直在用。所谓“分配陈述”,就是我们常说的,给变量赋值,。 在Apex里,用等号 `=` 来赋值。你把等号右边的计算结果,交给左边的变量保管。 ```apex String welcomeMessage; // 1. 先声明一个字符串变量 welcomeMessage = '你好,同学!'; // 2. 再把具体的文字“分配”给它 Integer studentCount = 0; // 更常见的写法是声明和赋值一步到位 studentCount = 100; // 程序运行中,又可以给它重新分配新的值 ``` 你只要记住,,等号左边必须是个“容器”(变量),等号右边的值和这个容器的类型要匹配,,就这么简单。 --- ### 二、如何在Apex中编写注释 注释是写给你自己,或者未来接手的开发者看的。电脑会完全忽略它。好的注释能救命,坏的注释是噪音。我们来把这件事做对。 Apex有两种注释方式: ,1. 单行注释:用两个斜杠 `//`, 从 `//` 开始,一直到这行结束,都是注释。 ```apex Integer totalAmount = 0; // 初始化总金额为0,准备开始累加 // 下面的代码用于计算折扣,暂时因为业务调整而跳过 // calculateDiscount(totalAmount); ``` 什么时候用? 解释你,为什么,这么写,而不是你写了什么。`totalAmount = 0` 本身就很清楚了,不需要画蛇添足写“把0赋值给总金额”。 ,2. 多行注释:用 `/* ... */`, 把需要注释的一大段内容包在中间。 ```apex /* * 这个方法是整个订单处理流程的核心,由“结账按钮”触发。 * 修改前请务必确认最新的业务规则文档(文档编号:DOC-2024-001)。 * 创建人:张老师 * 创建日期:2024年1月15日 */ public void processOrder() { // ... 一些重要的代码 } ``` 这个非常适用于写在类、方法或复杂逻辑的开头,作为“小说明书”。 记住,注释的最高级形态是 ,“干净的代码本身就是注释”,。把变量名起得有意义(比如用 `customerEmail` 而不是 `ce`),把长逻辑拆成小方法,这些比添加一大堆解释性注释更有效。 --- ### 三、数据类型之间转换的规则 数据需要在不同类型之间流动,就像水有时需要变成冰,有时需要变成蒸汽。但转换不是随心所欲的,有规矩。我们分两种: ,1. 隐式转换(自动转换), 这就像把一杯水倒进一个空桶里,Apex非常聪明,可以帮你自动完成。原则是:,从小范围、低精度的类型,向大范围、高精度的类型转,永远安全。, 比如: - `Integer` 可以自动变成 `Long`, `Double`, `Decimal`。 - `ID` 可以自动变成 `String`。 ```apex Integer stuNum = 15; Long longNum = stuNum; // 完美!整数自动变成长整数 String recordId = '001abc'; // 假设这是个ID ID myId = recordId; // 报错!String不能自动变成ID,必须显式转换 ``` ,2. 显式转换(手动转换), 这就像把冰化成水需要加热,把水变成冰需要降温一样,你需要明确告诉Apex:“我知道可能有风险,但我确定要这么做。” 用,类型名括起来,的方式。 比如,反过来把大杯子里的水倒进小杯子,就有可能溢出。 ```apex Long bigNumber = 3000000000L; // Integer normalNumber = bigNumber; // 报错!因为Long的范围大于Integer,可能溢出 // 你需要显式地告诉编译器:“我知道风险,给我转!” Integer normalNumber = (Integer)bigNumber; // 同样,String转ID也是一样 String recordId = '001R000000XXXXXX'; ID myId = (ID)recordId; // 你确定这个字符串格式正确,强制转换 ``` 最常用、最安全的显式转换,是在数字和字符串之间: - `String.valueOf( myNumber )`:数字转字符串 - `Integer.valueOf( myString )`:字符串转整数。这个要小心,如果字符串里是“abc”,程序就会报错崩溃。所以通常会用try-catch包起来,或者先检查。 --- ### 四、数字数据类型的其他注意事项 最后,说几个关于数字的小坑,提前知道能省下你很多调试时间。 ,1. 精度与财务计算, 这一点我每次都会敲黑板强调:,涉及钱,永远用 `Decimal`。, `Double` 是给科学计算用的,它用二进制模拟小数,会有精度问题,就像 `1/3` 无法在十进制里精确表示一样。 ```apex Double badMoney = 0.1 + 0.2; // 结果可能是 0.30000000000000004 Decimal goodMoney = 0.1 + 0.2; // 结果精确地等于 0.3 ``` ,2. 字面量后缀, 当你直接写下一个数字时,Apex会猜它的类型。如果你想明确告诉它,就需要加后缀: - `L` 或 `l`:表示 `Long`。比如 `long num = 3000000000L;`,不加L会被当成Integer并报错(超出范围)。 - `D` 或 `d`:表示 `Double`。比如 `double price = 19.99d;` ,3. 除零问题, - `Integer` 除以零,会直接抛出“不能被零除”的异常,程序崩掉。 - `Double` 或 `Decimal` 除以零,则不会报错,而是得到一个特殊值——正无穷大、负无穷大或者 NaN(不是一个数字)。这会让你后续的计算变得一塌糊涂还很难查。所以,,做除法前,养成好习惯,总是先判断除数是不是0。, 好了,这几个支撑性的知识点我们也说完了。它们就像编程路上的安全护栏和指路牌,让我们的代码既健壮又清晰。下一次,我们就可以开始正式接触在Salesforce里最激动人心的部分,如何直接操作数据库了。我们先休息一下。
今天我们来聊聊 Apex 里的“分配陈述”,也叫赋值语句。 简单说,分配陈述就是把一个值放进一个变量里。它的样子通常是一个变量名,后面跟一个等号,再跟上一个新值表达式,或者一个内联的 SOQL 查询结果。 比如,你可以这样写: `Integer i = 5;` 这里把 5 赋给了整数变量 i。 或者你直接写一个查询: `List<Account> accounts = [SELECT Id, Name FROM Account];` 这条语句会把从数据库查到的账户列表,直接放进 accounts 这个变量里。 在等号的左边,我们称为 LValue,也就是左值。 左值可以是一个普通的变量,比如 i; 也可以是一个解引用的列表元素,比如 myList[0],代表列表里的第一个位置; 还可以是 sObject 上的字段引用,比如 acc.Name,意思是一个账户记录的名字字段。 这些都能出现在等号的左边,接受一个新值。 这里有个内存管理上很重要的点:Apex 里的列表赋值是“按引用”传递的。 如果我们写出 `List<Integer> a = new List<Integer>();` 然后又写 `List<Integer> b = a;` 那这时候 b 和 a 实际上指向的是内存里的同一份列表数据。 如果你通过 b 往里面添加一个元素,你再去看 a,这个元素也会出现在 a 里面。 因为它们两个引用的是同一块内存。这一点在处理列表时要特别留意,防止意外修改了不该改的数据。 除了最基本的等号,Apex 还提供了很多方便的复合赋值运算符。 比如,`i += 3;` 就相当于 `i = i + 3;` `i *= 2;` 相当于 `i = i * 2;` 还有 `/=、|=、&=`,以及自增 `++` 和自减 `--`。 就像你写 `i++;` 其实就是让 i 的值加 1。 整体来说,这一页就是让你理解赋值语句的各种形式,以及赋值时引用传递的特点。 掌握了这些,你就能在代码里灵活地给变量、列表元素或字段设值,一步一步写出完整的业务逻辑。
同学,咱们今天聊聊Apex里的类型转换。Apex在类型转换上是非常严格的,你可别想着它能随便帮你自动转来转去,大部分情况下,你得明明白白告诉它:“我要把这个类型转成那个类型。” 不过呢,数字类型有一个自己的小王国,它们有个高低顺序,从低到高依次是:Integer(整数),然后是Long(长整数),再是Double(双精度浮点数),最高的是Decimal(高精度小数)。如果一个数字类型的变量,是低级别的,比如Integer,它可以随意赋值给高级别的类型,比如Long或者Double,反过来可不行。这就是隐式转换,不用你写任何代码,系统自动帮你处理。 其实,这种自动的隐式转换,Apex只允许很少的几种情况。我给你数一数:从Integer到Long,Integer到Double,还有Long到Double,这几种数字升级是允许的。另外,所有数字类型,当你用加号和一个字符串连接时,它们会自动转成String。比如说,`'编号' + 123` 这样写,123就会变成字符串。还有,ID类型可以隐式转成String,反过来String也能隐式转成ID,但要注意,String转ID时,运行时会检查格式是不是合法的18位或15位ID,不对的话就会报错。 除了上面说的这寥寥几种,其他所有转换都必须用显式的方法。你得分清楚,显式转换常用的有这几个:`String.valueOf()`可以把各种东西变成字符串;`Decimal.valueOf()`能把字符串转成Decimal;还有`Integer.valueOf()`或者直接用`.intValue()`把其他数字截断成整数。这些方法得根据你要转换的源类型和目标类型来选。 记住这个原则:级别低的数字自动能升到高级别,但要把高级别数字压到低级别,或者数字和字符串来回倒,必须用显式方法,ID跟字符串互相转虽然隐式也行,但最好也养成显式的习惯,代码可读性会更好。好了,这就是Apex类型转换的核心,遇到具体情况再查查具体方法就行。
同学们,咱们来聊聊Apex里面关于数据类型的一些重要注意事项。这些细节虽然基础,但写代码的时候一不小心就会踩坑,所以大家集中精神。 先看数字字面量。在你写一个普通的数字,比如5,Apex默认会把它当成Integer类型。如果你希望它是Long类型,就得在后面加一个大写的L,像5L这样。如果想要Double或者Decimal类型呢?那就在数字后面加上“.0”,比如5.0。这个规则很简单,但一定要记住,要不然类型不匹配的时候排查起来很麻烦。 接下来是溢出问题,这个比较隐蔽。在Apex里,如果你做算术运算,算出来的结果超过了这个类型能表示的最大值,它不会给你抛异常报错,而是会把值“回绕”。啥叫回绕呢?比如说Integer类型的最大值大约是21亿,你再加1,结果会突然变成负的21亿,从最小负值重新开始。这样你的数据就错得离谱了,但程序不吭声,调试起来很困难。所以,在做乘法或者一些可能产生大数字的运算时,要保证你用的数据类型足够容纳中间结果,该升级成Long或Decimal的时候一定要提前转换。 再看除法,这个坑也常见。当两个Integer或者Long相除,Apex执行的是整数除法,直接咔嚓掉小数部分。哪怕你把结果赋值给一个Double变量,分数也早就没了。举个例子:`Integer a=5, b=2; Double c = a/b;` 这时候c会是2.0,不是2.5。因为a/b先算出了整数2,然后才隐式转成2.0。想要保留小数,你得提前把其中一个操作数搞成Double或Decimal,比如写成5.0/2,或者强制转换一下,`(Double)a/b`。这点要格外留心。 最后是日期转换。Apex允许你直接把Date类型的值隐式或显式地转成Datetime类型,转换后时间部分会自动填零,也就是午夜0点。这很方便,但别误以为它会帮你带上某个时刻的时间,需要具体时间的话,还是得自己构造Datetime实例。 好了,这几条记住,写代码时多留个心眼,数值处理就能稳稳当当的。咱们继续。