DEX455

Apex 类、对象与接口完全指南

课程介绍

Apex 类、对象与接口完全指南:深入覆盖类定义语法(含全部修饰符和版本化行为)、变量与方法(含值传递vs引用传递完整示例)、构造函数重载与链式调用、访问修饰符(private默认/protected/public/global)、静态与实例成员(静态初始化代码/跨触发器通信)、Apex属性(自动属性/静态属性/访问器修饰符)、继承与多态(完整Marker/YellowMarker示例)、接口(PurchaseOrder示例)、自定义迭代器(Iterator/Iterable)、完整关键字参考(final/instanceof/super/this/transient)、共享模式(with/without/inherited sharing含触发器行为)、全部13种注解详解以及类与集合的类型转换。

课程章节

本课程共有 20 个章节

  • 1

    Classes, Objects, and Interfaces

    第 126 页

    来,我们聊聊Apex里的类这个概念。你可以这么理解:类就像一份蓝图,专门用来创建对象。这个思想其实借鉴了Java,所以如果你接触过Java,会感觉很熟悉。 在Apex里,你写的每一个类本质上都在做三件事:定义这个类叫什么、它能包含什么(比如变量和方法),以及对象可以怎么被创建出来。对象呢,就是根据这个蓝图造出来的具体东西。每个对象都有两部分:状态和行为。状态就是对象自己知道的信息,比如一个学生对象知道自己的名字和分数;行为就是这个对象能做的事情,比如可以计算成绩等级。 一个类里可以放很多东西:成员变量来保存状态,方法来实现行为。你还可以在类里面定义内部类、自定义异常类型,甚至写一段初始化代码,这些都在这一章里会详细讲。 而且,Apex支持完整的面向对象特性:你可以用接口来定义行为规范,让不同的类都实现同一个接口;可以用继承来扩展已有的类,复用代码;类还有版本控制,可以平滑升级;属性提供了一种简洁的读写方式,让代码更干净。 所以重点是,类就是你构建Apex程序的基本单元,理解了类,你就掌握了Apex面向对象编程的核心。接下来我们会深入每一点,让你能用好这些工具。

    查看详情
  • 2

    Apex Class Definition

    第 127 页

    好,同学们,今天咱们聊聊Apex中类是怎么定义的。这个话题其实不复杂,我一边讲你一边跟着想,很快就能记住。 首先,在Apex里,类可以放在两个地方:一种叫“顶级类”,也叫外部类,就是直接写在文件最外层的类;另一种叫“内部类”,是写在一个类的花括号里面。你可以理解为:一个大的包装盒里再装一个小盒子。不过要记住,Apex只允许套一层,你不能在大盒子里的小盒子里再套一层盒子,这是不允许的。 接下来,咱们看定义一个类的时候,要写哪些东西。一般来说,你先要写一个访问修饰符。对于顶级类,你必须写`public`或`global`,这决定了这个类能被谁看到、被谁使用。然后,你还可以根据需要,加上一些定义修饰符,比如`virtual`、`abstract`,或者和共享规则有关的`with sharing`、`without sharing`。这些修饰符是可选的,不用也没问题。之后,就是关键字`class`,接着是类的名字。再往后,还可以选择使用`extends`继承一个已有的类,或者用`implements`来实现一个或多个接口。 这里有个很关键的规则:一个类可以同时实现多个接口,但它只能继承一个父类。也就是说,Apex不支持多重继承。你只能有一个“亲爹”,但可以学会很多“技能”。这个规则一定要记牢。 最后,咱们说内部类的访问修饰符。如果你用`private`,那这个内部类就只能在它所在的这个外部类里面被认识,外面完全看不见。而且,如果你定义内部类的时候不写访问修饰符,默认它就是`private`的。所以日常开发中,如果你不想让外部随便用,其实直接写内部类不加修饰符就行,因为默认私有。 你看,是不是很简单?总结一下:顶级类必须public或global,内部类最多一层,类可以单继承、多接口实现,内部类默认私有。把这些记扎实,以后定义类的时候就不会迷糊了。

    查看详情
  • 3

    Class Variables & Methods

    第 128 页

    各位同学,大家好! 今天我们来看一个很基础但又特别重要的知识点——在Apex里怎么声明变量和方法,以及当你把数据传给方法时,里面到底发生了什么。 好,先说变量。 想要在Apex里创建一个变量,你需要告诉系统几件事: 第一,可选的修饰符,比如public(公开)、final(不可变)或者static(静态)。这些不是必须的,有需要才加。 第二,就是这个变量要存什么类型的数据,是整数Integer,还是字符串String,又或是自定义的对象。 第三,给变量起个名字,然后可以选择性地给它一个初始值。 比如,你可以写: `public Integer count = 0;` 意思就是声明一个公开的整数变量叫count,初始值0。很简单对吧。 再来看方法。 方法就像是一个能执行特定任务的小工具箱。声明它的时候,同样也要逐步告诉系统你的意图: 先是可选的修饰符,常见的有public、protected(受保护)、override(覆盖)、static等等。接着就是方法的返回数据类型,也就是说这个方法执行完后,会还给调用者一个什么类型的结果。如果它只是去干活,不返回任何东西,那就用关键字 `void` 来表示没有返回值。 然后就是参数了。参数要写在括号里,每个参数前面都得带数据类型,多个参数用逗号隔开。Apex规定一个方法的输入参数最多可以到32个,这对绝大多数场景都够用了。 最后,用一对花括号把方法的主体逻辑包起来,告诉它具体做什么。 举个例子: `public Integer add(Integer a, Integer b) { return a + b; }` 这个add方法接收两个整数,计算它们的和,然后返回一个整数结果。 好,现在大家重点要理解的是参数传递时的行为,这点很多初学者容易搞混。 Apex在这里分两种情况: 第一种,原始数据类型,也就是Integer、String、Boolean、Date这些。当你把它们作为参数传给方法时,传递的是值的副本。也就是说,方法内部对这个参数做的任何修改,都只活在方法内部,外面原始的那个变量的值并不会跟着变。这叫做“按值传递”。 第二种,非原始类型,比如sObject(像Account、Contact这些)、List(列表)、Map这些。它们传递的是引用,你可以理解成把“遥控器”交给了方法,而不是把整个对象复制一份。 这样你就拥有了操作原数据的权力——在方法里面,你可以直接修改这个对象里的字段值,比如改一改Account的Name、Phone,这些改动是会影响到外部的。 但这里有一个重要的限制:你不能把方法里的这个参数引用完全指向一个新的对象。什么意思呢?假设方法接收到一个Account对象,你可以在里面更新它的字段,但不能把它重新赋值为另一个新new出来的Account对象,如果你这么做了,那也只是改变了局部这个遥控器的指向,并不会影响到外部原来的引用。 那我们来想一个场景:你写了个方法,传入一个Account列表,你想给列表里每个账户的Rating设成‘Hot’,这个完全没问题,因为你在修改字段。但如果你在方法内部新创建一个空列表,然后把参数指向这个空列表,想让外边的引用也变空,这是做不到的。非基元类型是引用传递,但只允许你修改内容,不允许你替换引用本身。 这样一来,在日常开发中,你就能很清楚地知道,什么时候需要小心别在方法里不小心改了对象,什么时候可以利用这个特性去批量更新数据。 好了,今天就先讲到这儿。变量声明的格式、方法定义的要素,以及参数传递的两种行为,你只要动手写几个例子,马上就能掌握了。下次我们继续深入Apex其他好玩的地方。

    查看详情
  • 4

    Using Constructors

    第 129 页

    好,咱们今天来聊聊Apex里的构造函数。你可能会觉得这名字有点生硬,其实把它想象成一张“出生证明”就行。每当我们根据类的蓝图,用`new`这个关键字去创建一个新对象时,系统会立刻自动调用的一段代码,就是构造函数。它的任务呢,就是帮我们把这个新生的对象初始化好,比如给它赋予一些初始值。 那是不是每写一个类,都要自己编一个构造函数?当然不是。如果你不在类里写任何构造函数,系统会免费送你一个——我们叫它默认的无参构造函数。这个默认构造函数的可见性,和类本身的可见性是一样的,比如类是`public`,那这个默认构造函数也就是`public`的。所以你平时写`Account acc = new Account();`,就是靠的它。 但要注意一点:构造函数的写法和普通方法很像,可有两个关键区别。第一,它绝对没有返回类型,连`void`都不写。第二,构造函数不会被继承,子类不能自动获得父类的构造函数。 再讲个容易踩坑的地方。假设你给自己的类写了一个带参数的构造函数,比如`public MyClass(String name)`,那这时系统就不会再送你那个免费的无参构造函数了。如果你还希望别人能用`new MyClass()`这种方式来创建对象,那就必须自己再补写一个无参的构造函数。简单说就是:只要你自己动了手,不管写了哪一种,默认的无参构造函数就消失了,得自己包办。 另外,构造函数是可以重载的,也就是在一个类里写好几个构造函数,只要它们的参数列表不同——参数个数、类型或者顺序不一样就行。这样就能提供多种初始化对象的方式。 最后,还有个很有用的技巧,叫构造函数链接。一个构造函数可以通过`this(参数)`这样的语法,去调用本类里另一个参数匹配的构造函数。这样做的好处是避免重复代码,把公共的初始化逻辑集中起来,让代码更干净。 总之,构造函数就是为对象诞生那一刻准备的代码,它让对象一出现就有了该有的样子。记住这几个要点,用起来会很顺手。咱们下一部分再见。

    查看详情
  • 5

    Access Modifiers

    第 130 页

    同学们,大家好,今天我们来聊聊 Apex 里访问修饰符那些事儿。这些修饰符就像是给方法和变量加上的“门禁卡”,控制着谁可以进来访问、谁不能。在 Apex 里,我们有四个访问级别:private、protected、public 和 global。 首先,如果你在一个方法或变量前面什么都不写,也就是不加任何修饰符,那它默认就是 private 的。意味着只有在定义它的那个类里面,其他方法才能看得见、用得着,出了这个类,外面就完全不知道它的存在了。这个默认行为很安全,帮我们藏起内部实现细节。 接下来是 protected。当你在方法或变量前写上 protected 关键字,它就变得稍微“友好”了一点。它允许两种朋友访问:一种是本类的内部类(也就是嵌套在里面的类),另一种是继承了这个类的子类。注意,即使是子类,也必须是在命名空间内通过继承关系才能看到。这让类的家族内部可以共享一些逻辑,但又不会完全暴露给外人。 然后是 public。在 Apex 里,public 的意思跟在 Java 里很不一样,大家一定要留心。在 Apex 中,public 只表示“在同一个命名空间(或者说同一个包、同一个应用程序)内是公开的”。换句话说,只要你写的代码和这个类在同一个 managed package 或者同一个 namespace 下,你就可以随便访问它;但假如另一个包或外部应用想调用,对不起,public 是挡不住的。所以,如果你想实现 Java 中那种“世界上任何代码都能访问”的效果,就不能用 public。 那真正能做到完全开放、跨应用程序访问的是哪个呢?就是 global。没错,global 修饰符让方法或变量能被任何 Apex 代码访问,不管它在哪个命名空间、哪个包里,甚至是在外部调用你应用程序接口的时候。这听起来很强大,但我们 Salesforce 官方其实非常不建议大家随便用 global。为什么呢?因为一旦你用了 global,就等于和其他应用建立了一种强依赖关系。后续你想修改这个方法签名或者删掉它,就会非常困难,因为你不知道有多少外部的代码在依赖它,维护成本会变得很高。所以,除非你明确知道这个方法需要被外部订阅者或扩展包调用,否则尽量别用 global。 好,我们简单总结一下:默认不写就是 private,仅本类可见;protected 开放给内部类和子类;public 在同一命名空间内公开;global 则是完全公开、跨应用的。记住 Apex 里的 public 不等同于 Java 的 public,Java 的 public 是 Apex 里的 global。下次写代码时,先想想“这个方法和变量到底需要让谁看见”,再决定用哪个修饰符,这样你的代码会更安全,也更容易维护。 今天的知识点就讲到这里,大家有什么疑问吗?我们下次继续深入 Apex 的其他特性。

    查看详情
  • 6

    Static and Instance Members

    第 131 页

    大家好,今天我们来讲一讲Apex里静态成员和实例成员的区别,这个概念如果搞清楚了,写出来的代码会更干净、更高效。 首先,Apex里你可以有静态的方法、静态的变量,还能写静态的初始化代码块。但是注意一点:类本身不能是静态的,只有类里面的成员才能标成静态。而且这些静态的东西,只能放在最外层的类里,内部类是放不了静态成员的。 那么静态成员到底有什么特点呢?它们跟类绑在一起,而不是跟某个具体的对象绑在一起。意思就是说,哪怕你用这个类 new 了好几个对象,静态变量也只有一个副本,被所有这些对象共享。比如你有一个记录总数的静态变量,不管哪个对象去改它,大家看到的都是同一个数值。这个变量什么时候初始化呢?在类第一次被加载到内存的时候,只初始化一次。在同一个事务里,静态变量的值会一直保留着,直到事务结束。一旦事务结束,下个事务再进来,静态变量就重置了,重新开始。 再来看实例成员,它们就是跟每个具体的对象绑定的。每当你 new 一个对象,实例变量就会重新创建一份,每个对象都有自己独立的值。每次实例化对象的时候,实例初始化代码块都会运行,而且它是在构造函数之前执行的,你可以用它来做一些对象创建时的准备工作。 另外还有静态初始化代码块,它是用 static 关键字包裹的一段代码,也在类第一次被使用的时候运行,而且这辈子只运行这么一次。通常用来做一些一次性的设置,比如加载一些静态配置。 总结一下关键点:静态的东西,属于类,全局共享一份,在事务里持久,跨事务重置;实例的东西,属于对象,每创建一个对象就生成一份,跟着对象走。把这两者区分清楚,你在设计代码时就能更好地控制数据的作用范围和生命周期了。 这样讲,是不是清晰了很多呢?如果有疑问,随时提出来我们再讨论。

    查看详情
  • 7

    Apex Properties

    第 132 页

    同学们,咱们来聊聊Apex里的属性。你可以把属性想象成一种带上了“小管家”的变量。普通变量就是直接存取值,但属性呢,在存或取的那一刻,可以让你干点儿别的事——比如检查数据合不合法,或者提醒一下“诶,有数据变动了哦”,甚至可以把藏在其他地方的数值计算出来给你。 属性的核心是它里面有一对代码块,一个叫 `get`(负责取值),一个叫 `set`(负责赋值)。如果你暂时不需要添加任何逻辑,完全可以只写一对花括号,里面什么都不放,这就成了“自动属性”,用起来跟普通变量一模一样,但保留了以后随时加逻辑的弹性。 那说到加逻辑,最常见的场景就是:你想在值被改写前做验证,比如不允许年龄为负数;或者在读取时,自动拼接上一些默认的格式。这些都可以在 `get` 和 `set` 块里轻松实现。 还有个重要的点:属性的 `get` 和 `set` 可以有自己的访问修饰符,但这个修饰符必须比属性本身的更“私密”。比方说,你定义一个公共属性(public),那么你可以让它的 `set` 变成私有的(private),这样外面可以读,但只有类内部能修改它;反过来,如果你想让外面能写但不能读,就可以把 `get` 设成私有,但这种做法不太常见。要记住,访问符的限制级别只能提升,不能放松——公共属性的 `get` 不能是公共,因为那就没变严;但改成私有,就合规了。最经典的用法就是 `public 类型 名字 { get; private set; }`,实现了对外只读、对内可写的控制。 好了,这一页就先聊这么些,大家脑子里记住这个画面:属性是带了“门禁安全系统”的变量,进门出门你都可以做检查或加工,非常灵活。下页我们看看实际代码怎么掏出来用。

    查看详情
  • 8

    Extending a Class

    第 133 页

    同学们,咱们今天来说一个非常非常重要的概念,叫做“继承”。 你可以这么理解:如果已经有一个现成的类,它能做很多事情,但你想让它更特别一点、更符合你的业务需求,那怎么办?咱们不必把原来的代码全部重写一遍,只要让一个新的类去,扩展,它就行了。 用关键词来说,就是“,extends,”。比如你写一个“汽车”类,然后你想做一辆“跑车”,那跑车就继承了汽车所有的本事——什么踩油门啊、刹车啊,这些方法和属性全都有了。 但跑车总得有点不一样,对吧?所以我们可以用,override,这个关键字,去,重写,原来那些用,virtual,标记过的方法。比如说原来汽车的“加速”比较普通,我们在跑车里把它重写成特别猛烈的加速——同一个方法名,但实现完全不同。这个其实就是面向对象里常说的,多态,,同一个动作,不同对象表现出不同行为。 还要记住一个规则:在Apex里,一个类,只能扩展一个父类,。你要是想做“既像汽车又像飞机”的类,靠单一继承可不行。但是呢,我们可以用,接口,来突破这个限制。一个类可以实现,多个接口,,这样就能扮演多种不同的角色。 对了,接口本身也可以扩展。一个接口可以继承另一个接口,那么所有继承来的方法和属性,也都必须被实现。 总结一下:,继承帮我们重用代码,重写帮我们定制行为,多态让程序更灵活,而接口保证了多重能力,。这节课的内容就这些,大家先吸收一下,咱们下节再见。

    查看详情
  • 9

    Interfaces

    第 134 页

    接下来咱们聊聊Apex里的接口。你可以把接口想象成一个特殊的类,但它的方法里完全没有任何实现代码。接口里只有方法的定义,比如方法名、参数、返回类型这些,但方法体是空的。如果你要用这个接口,你就得写一个类,专门去实现这个接口,然后把里面所有的方法都补上真正的逻辑。这种设计把“要做什么”和“具体怎么做”分开了,给了我们一层抽象。还有一点很重要:你定义一个接口,其实就等于定义了一种新的数据类型。之后你可以在任何普通数据类型能用的地方,使用这个接口名来声明变量。但要注意,你赋值给这个接口变量的对象,必须是实现了该接口的类的实例。这样一来,代码就更灵活,也更容易扩展。

    查看详情
  • 10

    Custom Iterators

    第 135 页

    同学们,今天我们来讲一个在Apex里特别有用的概念,就是迭代器和可迭代对象。简单说,迭代器就是帮你一个一个地访问集合里面所有东西的工具。 想象一下,你有一个装着很多物品的箱子,你想依次拿出每个物品看看。迭代器就像你的手,每次拿一个,拿完还能告诉你“还有没有下一个”。在代码里,我们有两个关键方法:hasNext()用来问还有没有下一个;next()就是取出下一个。如果你想自定义自己的迭代器,就需要写一个Apex类,实现这个Iterator接口,里面完整地写出hasNext()和next()。 但是,如果你自己发明了一种新的数据结构,不是用Salesforce标准的列表,那你就要让这个数据结构能够被遍历,这时候就要实现Iterable接口。这个接口只有一个方法,叫iterator(),它返回一个迭代器对象。这样别人拿到你的数据结构,调用iterator()就能得到一个迭代器,然后就能用hasNext和next来逐个访问你的数据了。 还要提醒一下,这两个接口里的方法,声明时必须是global或者public,这样其他代码才能调用。 最后有一个非常实用的点:在批处理Apex中,也就是实现Database.Batchable接口时,你可以返回一个自定义的可迭代对象作为start方法的数据源,而不仅仅是查询返回的列表。这就意味着你可以用迭代器的逻辑来决定批处理每次处理哪些记录,让批处理更加灵活。 总结一下,Iterator让你能一个个拿东西,Iterable让你的整个集合能被遍历,而自定义迭代器还能用在批处理里控制数据流。是不是感觉这些概念一下子清晰了?

    查看详情
  • 11

    Keywords — final, instanceof, super, this

    第 136 页

    同学们,我们来聊聊Apex里几个特别常用的关键字。它们就像一些小工具,用对了能让你的代码更安全、更清晰。 首先是 ,final,。你可以把它理解成“最终版,不能改”。一个变量如果被声明为 final,那就只能赋值一次,之后再改就会报错。如果把 static 和 final 组合在一起,就能定义一个常量,比如公司的固定税率,永远不变。另外需要注意,在 Apex 中,方法和类默认就是 final 的,也就是默认不能被子类重写。如果你想允许别人覆盖,就得用 ,virtual, 关键字,相当于给方法或类打开一扇“允许重写”的门。 接着是 ,instanceof,。它是个类型检查器,在程序运行时帮你判断某个对象究竟属于哪个类。比如你拿到一个笼统的“动物”对象,用 instanceof 一问,它就能告诉你到底是“猫”还是“狗”,避免类型转换出错。 ,super, 关键字是用来联系父类的。它在子类里可以调用父类的构造方法,帮父类完成初始化;如果子类重写了父类的某个方法,你又想用父类原来的实现,也可以通过 super 来调用,而不是从头再写一遍。 ,this, 关键字就指“我自己”。当方法的参数名和类的成员变量重名时,用 this 就能明确区分,告诉编译器我要用的是实例自己的那个变量。另外,在一个构造方法里,还可以用 this 去调用本类的另一个构造方法,省得代码重复。 ,transient, 比较特殊,用在变量上,表示这个变量的值“不可持久化”。在 Visualforce 页面里,视图状态会保存很多数据,但 transient 变量不会被保存或传递,就像个临时便签,翻页后就忘了。处理一些临时计算结果时加上它,能有效减少数据量,提升性能。 最后是 ,with sharing, 和 ,without sharing,。这俩关键字控制的是代码是否执行共享规则,也就是是否尊重当前用户的记录权限。with sharing 表示遵守规则,用户只能看到自己有权限的数据,像一个普通员工走流程。without sharing 则是绕过权限,以系统模式运行,可以看到全量的数据。用的时候一定要弄清是否真的需要绕过安全限制,保护好数据安全。 好了,这几个关键字你记住了吗?把它们掌握好,你写的 Apex 代码会更加健壮、安全,也更易懂。我们下次再见!

    查看详情
  • 12

    Sharing Keywords — with, without, inherited

    第 137 页

    同学们,咱们今天来聊聊Apex中的共享规则,也就是控制代码是否遵守组织和记录共享权限的关键字。你可能会在类定义上看到`with sharing`、`without sharing`或者`inherited sharing`这些声明。它们的作用是什么呢?简单说,就是告诉Salesforce执行这段代码时,要不要参考当前用户的访问权限和共享规则。 我们先看最直接的:`with sharing`。如果你在类上写了`with sharing`,那么这个类里所有的数据库操作和SOQL查询都会严格遵循当前用户的权限,比如对象级别、字段级别和记录共享规则。这能保证数据安全,防止越权访问。 反过来,如果不想要这些限制,比如你要做一些管理员级别的批量处理,就可以用`without sharing`,让代码在系统模式下运行,忽略当前用户的共享规则。 还有一个比较灵活的选项叫`inherited sharing`。这个关键字是让类本身不固定共享模式,而是由调用它的那一方来决定。如果调用的地方是`with sharing`上下文,那这个类就以共享模式运行;如果调用方是`without sharing`,那它也跟着不共享。这就给了你更多的运行时弹性。 从API 67.0版开始,Salesforce做了一个重要的默认行为调整:一个类如果没有明确写任何共享声明,它会默认按照`with sharing`执行。而在旧版本里,这种省略的情况下是默认`without sharing`的。所以,为了让你的代码行为清晰、不因版本迁移出现意外,官方现在的强烈建议是:只要你的类涉及数据库操作或SOQL查询,就一定要显式地加上共享声明,不管是`with sharing`、`without sharing`还是`inherited sharing`,明确写出来,对自己、对其他阅读代码的人都有好处。 再补充一点关于触发器的特殊情况。触发器默认是不共享的,它们本身运行在系统上下文,没有共享限制。但是请注意,在触发器内部执行的数据库操作,现在的默认行为是运行在用户模式下,也就是会尊重当前用户的字段权限和共享规则。这个细节很容易搞混,所以处理触发器的逻辑时,也要多留意权限控制的期望。 总结一下,记住这条原则:永远别让你的类共享声明空着,根据实际场景选一个,清楚表达你的意图,这样代码才能安全又可靠。咱们写代码,先讲清楚规矩,后续维护就少踩坑。

    查看详情
  • 13

    Annotations Overview

    第 138 页

    同学们,咱们这节课来说说 Apex 里的“注释”,或者叫注解,英文是 Annotation。它跟我们平常写双斜杠那种解释代码的注释不一样,这个是专门用来修改类或者方法的行为的,就跟 Java 里的注解几乎一模一样。 你可以把它想象成贴一个小标签,告诉 Salesforce 平台:“嘿,这个方法或类,你要用特殊的方式对待它哦。” 那怎么写呢?很简单,就是一个 @ 符号,后面跟上关键字。比如大家以后会经常碰到的 @isTest,用来标记测试类;或者是 @AuraEnabled,让方法能被 Lightning 组件调用。你把它放在哪呢?一定要紧挨着方法或类的定义之前,单独一行。别塞到代码里面去,那样就失效了。 比如说,你想把一个方法标记为未来执行的异步方法,就可以这样写: @future public static void doSomethingAsync() { ... } 看到了吧,@future 这一行就贴在方法签名上面。 那如果我既想让它能被 Aura 组件调用,又需要它异步执行呢?很简单,你可以用一个以上的注释,每个注释单独占一行,叠在一起。像这样: @AuraEnabled @future public static void myMethod() { ... } 这样,两个标签的需求就都满足了。但是一定要注意,不是所有注释都能像乐高积木一样随便拼在一起的。有些注释彼此是冲突的,不能同时使用。具体哪些不能混搭,文档里每个注释的介绍页面都会有详细说明。所以我们用的时候,要是看到开发控制台报错,或者说行为不对劲,记得回头去查一下那个注释的限制。 总之一句话:Apex 的注释就是用 @ 开头的标签,放在类或方法前面,用来给它附加特殊能力。可以单用,也可以多行连用,但要遵守各自的规则。把这一点记牢,后面的开发会顺手很多。好了,这块就这么简单,咱们继续往下看。

    查看详情
  • 14

    @AuraEnabled & @Deprecated

    第 139 页

    今天我们来看两个在Apex开发中非常实用的小注释,一个是,@AuraEnabled,,另一个是,@Deprecated,。这两个东西虽然写起来很简单,但用好了能让你的代码更灵活、更清晰。 --- 首先说说,@AuraEnabled,。 它的作用就是告诉Salesforce:“嘿,这个方法,Lightning组件要用到它!” 也就是说,加了`@AuraEnabled`注解,这个方法就可以同时被客户端(也就是Lightning组件)和服务器端直接调用。 而且从API 44.0版本开始,你还可以多加一个参数叫`cacheable=true`。它有什么好处呢? 就是如果组件多次请求同样的数据,不需要重复跑服务器逻辑,直接从缓存里拿结果就行了,这样性能会更好。 简单总结:想让你的Apex方法在Lightning组件里用,就记得加上`@AuraEnabled`,需要缓存就写`@AuraEnabled(cacheable=true)`。 --- 再来看,@Deprecated,。 它表示“这个方法、类或变量已经过时了,以后的新版本可能不再支持”。 特别是在做托管包(managed package)开发的时候,随着需求变化,我们可能会重写一些代码。 但是老用户还在用旧方法,直接删掉会让他们出问题。 这时候就可以给旧的方法加上`@Deprecated`注解。 效果就是:新安装包的用户看不到这些已弃用的东西,但现有的订阅者还能继续用,给他们时间慢慢迁移到新方法。 等于给了一个缓冲期,既能让代码保持整洁,又不会影响现有集成。 注意啊,被标记为`@Deprecated`的方法即使不再推荐使用,但只要没被彻底移除,就还能正常执行。 --- 这样讲是不是很简单?用的时候记住这两点就行: - ,@AuraEnabled,:打通客户端和服务器,按需加上`cacheable=true`优化性能。 - ,@Deprecated,:给过时代码打个标记,提醒自己和他人别再用老方法了,但又给现有用户缓冲时间。 以后在写Apex类的时候,看到这两个注解就能明白它们是什么意思,也知道怎么用了。

    查看详情
  • 15

    @Future & @InvocableMethod

    第 140 页

    同学们,咱们今天来看两个在Apex里很实用的注释,@Future和@InvocableMethod。别看它们只是小小的标记,但用好了能帮你处理后台任务,还能让你的代码被流程、REST接口甚至Agentforce直接调用。 先说@Future。这个注释是用来告诉Salesforce:“嘿,这个方法不用马上跑完,等平台有空闲资源的时候,你帮我在后台异步执行就行。”所以它很适合处理那些耗时比较长、但又不需要立刻出结果的操作,比如调用外部Web服务、做复杂的计算等等。 不过呢,Salesforce现在更推荐大家使用Queueable Apex,也就是可排队的Apex,来替代这种未来的方法。为什么?因为可排队Apex比@Future更强大——它能返回一个作业ID,方便你跟踪任务跑完了没;支持传递复杂的非原始类型参数,不像@Future只能传基本类型;而且还能把作业串成链,一个跑完了自动触发下一个,逻辑清晰多了。所以如果你刚开始学异步处理,可以直接学Queueable,@Future作为了解就好。 再来看@InvocableMethod。这个注释就更有意思了,它能让你的方法变成一个“可调用操作”。啥意思呢?就是标记了这个注释的方法,可以被Apex代码、REST API、Flow(也就是咱们的流)甚至新出的Agentforce代理像搭积木一样直接调起来。这样,写好的逻辑就能在不同的地方重复使用,不用重复写代码。 调用的时候,输入和输出都是动态的,非常灵活。它还能支持描述信息,告诉调用方我需要什么参数、会返回什么。但有一点要特别注意:如果你的操作是批量处理的,那么输出的第i项必须严格对应输入的第i项。就是说,给第一个客户的计算结果,一定要放在输出的第一位,别搞乱了顺序,这样批量处理才能正确无误。 总结一下,@Future帮你异步,但现在更推荐用Queueable;@InvocableMethod让你的方法变成万能调用块,为流程和智能体服务,注意输入输出顺序就行。好,这页Slide的内容就讲这么多,咱们做个小练习巩固一下?

    查看详情
  • 16

    @IsTest & @JsonAccess

    第 141 页

    同学们,我们来看两个挺实用的注解,就是`@IsTest`和`@JsonAccess`。我会用大白话给你讲清楚它们干什么用。 先说说`@IsTest`。你在写Apex测试类的时候,肯定见过它,它就是专门用来标记“这段代码只做测试,不算正式业务逻辑”的。你可以把它贴在一个类上,也可以只贴在某个方法上。它还有几个小伙伴可以一起用,你记住: 一个是`SeeAllData`,比如写`@IsTest(SeeAllData=true)`,这么一来,你的测试就能看到组织里所有已有的数据了,不用再自己造数据;不过平时我们更推荐自己建数据,这样可控。 还有`OnStart`,这个是在安装包的时候才会用,用来在包安装完立刻跑一些测试逻辑。 然后是`IsFlow`,它让测试可以并行跑,提高效率,不过要注意别让并行测试互相打架。 另外还有一些修饰符,像`Beta`和`testFor`之类的,用得相对少些,将来遇到查一下就行。 好,再来看`@JsonAccess`。这个注解是控制类的实例能不能被序列化(变成JSON字符串)或者反序列化(从JSON字符串变回对象)。你可以在类定义前加上它,然后配上`serializable`和`deserializable`这两个参数,来强制规定上下文里到底让不让你做序列化。 比如你写`@JsonAccess(serializable=true, deserializable=false)`,那这个类的对象就只能转成JSON发出去,但不允许从JSON转回来。要是你违反了设定,比如硬要反序列化一个被禁止的类,运行时就会抛出一个`JSONException`,程序就告诉你“不行”。 简单来说,`@IsTest`帮你管理测试代码和环境,`@JsonAccess`帮你控制对象与JSON互转的权限。这两个注解用好了,代码会更安全也更清晰。 好了,这一页的概念先讲到这儿,有什么问题随时问我。

    查看详情
  • 17

    @NamespaceAccessible, @ReadOnly, @RemoteAction

    第 142 页

    同学们,我们来看这一页内容,它介绍了好几个很有用的Apex注解,以及一个非常重要的安全提醒。我们依次来理解一下,一定要认真听哦。 首先,第一个注解是 `@paceDeliverable`。这个名字你们可能觉得有点奇怪,它的作用其实很简单:就是当你在一个包里写了一个公共的Apex类,想让它被同一个命名空间下的其他包也能使用的时候,就可以加上这个注解。这样就能跨包共享代码了,知道吗? 然后我们看 `@ReadOnly` 注解。这个注解很强大,主要用在查询数据的时候。通常Salesforce对一个事务能查询的行数是有限制的,但如果你用 `@ReadOnly`,它就把这个限制放宽到最多可以查一百万行。不过有个代价,就是它会阻止所有的写操作,也就是TLR——事务里的DML操作都不能做。所以它很适合用来做大批量的只读报表查询。 接着是 `@RemoteAction`。这个注解是给Visualforce页面用的,它能让你通过JavaScript远程调用Apex里的方法。比如说,你在页面上点个按钮,页面不用刷新,直接就能调用后台的方法去获取数据或者做操作。但注意哦,被 `@RemoteAction` 标注的方法必须是静态的,而且访问修饰符必须是 `global` 或 `public`。这样才行。 最后,这里有一个警告,非常关键!它说:如果你在一个页面里添加了控制器或者控制器扩展,那整个Apex类里面所有的 `@RemoteAction` 方法,哪怕你没有在页面上用到它们,也会全部暴露给这个页面。这意味着,如果有人知道方法名,他就能尝试通过JavaScript调用那些你可能觉得“没用就不会有风险”的方法。所以一定要小心,不要在一个控制器类里乱写 `@RemoteAction` 方法,一定要只放那些页面真正需要的,并且做好权限检查。 好了,这一页的知识点非常实用,大家记住了吗?后面我们会结合实际例子更深入地讲解。现在先理解这些概念就好。

    查看详情
  • 18

    @TestSetup, @TestVisible & Apex REST Annotations

    第 143 页

    同学们,今天我们来讲几个在Apex开发中非常实用的注解,也就是开头带@符号的那些标记。这些注解就像给代码贴上的小标签,告诉系统该怎么特殊处理它们。 首先来看@testSetup。你在写测试类的时候,是不是经常每个测试方法都要重复创建相同的测试数据?很麻烦对吧?@testSetup就是专门来解决这个问题的。你只需要在一个方法上加上这个注解,然后把所有公共的测试数据创建逻辑写进去,比如插入几条客户、订单记录。这样,测试框架会先自动执行这个带有@testSetup的方法,把数据准备好,然后再去执行每一个测试方法。而且注意哦,每个测试方法跑完之后,这些数据会被回滚,就像什么都没发生过一样,所以测试之间不会互相干扰。记住,一个测试类里只能有一个@testSetup方法,不然会报错。 接下来是@TestVisible。这个注解就像给测试开了一扇特殊的后门。我们都知道,代码里有些私有(private)或者受保护(protected)的成员变量和方法,正常情况下,测试类在外面是访问不到的。但如果你在那些成员前面加上@TestVisible,那么测试类就可以暂时访问它们了。不过你放心,这只是测试期间的“特权”,对于非测试类的代码,这些成员的可见性完全没有变化,该私有的还是私有。所以@TestVisible让我们能更精细地去验证内部逻辑,而不用破坏正常的封装设计。 最后,我们来聊聊Apex REST注解,它能让你的Apex类摇身一变,成为一个RESTful Web服务。一共需要两个层面的注解:在类的声明上,你要加@RestResource,并且给它指定一个URL映射地址,比如'/Accounts/',这样外部请求就能通过这个地址找到你的服务。然后,在类里面的方法上,你根据不同的HTTP动词加上对应的注解:@HttpGet用来处理GET请求,通常用来查询数据;@HttpPost处理POST请求,一般用来创建记录;@HttpPut处理PUT请求,通常用来更新记录;@HttpDelete处理DELETE请求,用来删除记录;还有一个@HttpPatch,用来处理部分更新的PATCH请求。系统会根据外部请求的HTTP方法和URL,自动调用对应注解的方法,非常方便。 好了,今天这三个注解@testSetup、@TestVisible和Apex REST系列注解,你学会了吗?它们各司其职,能让你的测试和Web服务开发更加简洁高效。下次我们再来探索更多有趣的Apex知识。

    查看详情
  • 19

    Classes and Casting

    第 144 页

    好,我们来看一下Apex中的类型转换,以及instanceof关键字。这个概念其实不复杂,你把它想象成搭积木,每个积木都有自己的形状,有时候你需要把一个积木当作另一种形状来用。 首先,在Apex里,所有类型的信息在代码运行的时候都是可用的。也就是说,系统永远知道每个变量到底是什么类型,不会搞混。这给了我们一个方便的能力:强制转换,也叫类型铸造。 强制转换是什么意思呢?就是把一个类的实例,当成另一个类来用。但这里有个规矩:只有当一个类是另一个类的子集时,才能转。比如,你有一个父类叫“动物”,有一个子类叫“猫”。你可以把一只“猫”的对象,强制转换成“动物”类型,因为猫一定是动物。反过来,如果你手上是一个“动物”对象,你不能随随便便就把它当“猫”来用,除非它本质上就是一只猫。这就是“一个是另一个的子集”的意思。 咱们在写代码的时候,常常会碰到需要把对象从一种数据类型转成另一种的情况,这时就要用到强制转换的语法,就是在变量前面加个括号,里面写上目标类型。 另外,接口类型也可以强制转换。接口本身不是类,但你可以把实现了某个接口的对象,强制转换成那个接口的类型,或者把接口类型再转回具体的实现类,甚至是子接口。道理是一样的——只要它们在继承和实现关系上是兼容的。 不过,强制转换有个风险:如果你乱转,比如把一只狗硬当成猫来用,系统运行的时候就会报错,抛出运行时异常。为了避免这种情况,Apex提供了一个很友好的关键字:instanceof。你可以在做转换之前,先用 `instanceof` 检查一下这个对象到底是不是目标类型——就像是先问问“你是猫吗?”,如果它说“是”,你再把它当猫对待。这样就能安全地避免运行时错误。 总结一下,这页Slide的核心就三点: 第一,所有类型信息运行时都知道; 第二,强制转换必须基于有效的父子类或接口实现关系,不能乱转; 第三,为了安全,在转换前请用instanceof验证类型。 这样讲,是不是一下子就清晰了?以后遇到需要类型转换的地方,记住先检查再转换,代码就能稳稳当当不出错。

    查看详情
  • 20

    Collection Casting & Custom Types

    第 145 页

    同学们,今天我们来聊一聊 Apex 中集合的一个特别方便的特性,就是它怎么跟自定义的类、接口,以及 sObject 一起使用,并且还能在类型之间灵活地转换。 首先,咱们都知道,集合无非就是列表(List)和映射(Map)。它们不光能存基本类型,比如字符串、数字,还能直接存 sObject,比如 Account、Contact 这些。更进一步,你可以把自己写的类、实现的接口当作集合里元素的类型。比方说,你可以定义一个 `List<我的订单类>`,或者一个 `Map<Id, 我的客户类>`。也就是说,映射的键和值,都可以是用户自定义的数据类型,这样就能创建出完全契合你业务结构的对象集合。 重点来了——因为 Apex 在运行的时候,集合里的每个元素依然保留着它最真实的类型,所以 Apex 允许一种很安全的集合强制转换。听起来有点拗口,其实很好理解,就是“向上转型”。打个比方,假设你有一个父类叫 SecureOrder,然后有一个子类叫 CustomerSecureOrder,继承自它。现在,你手里有一个专门存放子类对象的列表:`List<CustomerSecureOrder>`。在 Apex 里,你可以直接把它赋给一个声明为父类类型的列表变量:`List<SecureOrder>`。这个过程,就是集合的强制转换。 这种转换之所以是安全的,因为你只是换了一个更宽泛的视角去看这个列表,列表里实际存的还是那些子类对象,你在取出来的时候,可以按照父类的标准去操作它们,这完全没问题。当然,你不能反过来把一个存着各种 SecureOrder 的列表强行当 `List<CustomerSecureOrder>` 用,除非里面的元素真的全都是子类的实例,否则运行时会报错。 地图也是类似的逻辑,不过只针对值这一侧。因为地图的键一旦定义了类型,就必须保持严格一致。规则是:如果地图 A 里存放的值类型,可以被安全地转换成地图 B 的值类型,并且它们键的类型完全相同,那么整个地图 A 就能被转换成地图 B。举个例子,你有一个 `Map<String, CustomerSecureOrder>`,就可以把它赋给 `Map<String, SecureOrder>` 变量。键都是 String,值从子类变成了父类,这个转换是允许的。 这种特性在编码的时候特别实用,能让你写出更通用的方法。比如一个方法接受 `List<sObject>` 参数,你就可以传入任何具体 sObject 的列表,像 `List<Account>`、`List<Contact>`,代码复用性一下就提高了。 好,这一页的核心就是:集合可以和类、接口自然结合,并且支持向父类型的安全转换,这让我们的代码既灵活又类型安全。大家理解这个思路了吗?我们下一节继续。

    查看详情