Let’s talk about what a method looks like syntactically.
access_modifier return_type method_name (data_type parameterName, data_type parameterName2) { // Method body }
access_modifier
should be substituted with the keywords public
, protected
, global
or private
. Similar to member variables, the default access modifier for methods will be private
and implicitly declared. You can explicitly declare a method as private
if you wish. private
methods can only be called from the class that declared it. public
methods can be called from any class in the namespace.
return_type
should be substituted with the datatype that will be returned when the method is done running. The datatype can be anything, including a custom class. It can also be nothing. If a method returns no data, the datatype can be marked using the keyword void
. Data is returned in the method body using the return
keyword. More on that in a second.
method_name
should be substituted with the name of a method. The same rules as variable names apply here. Method names can’t start with numbers and can’t contain special characters other than an underscore _
. The suggested stylistic naming convention here is camelCase
, just like the style convention with variables. Remember, Apex is case-insensitive, so it’s up to developers to drive capitalization consistency.
The (
and )
work the same as they do with constructors. They are used to provide a comma-separated list of variables and their datatypes to the method. If there are no parameters to pass in, it will be indicated with empty open and close parentheses ()
. The method’s name and parameter list come together to create a method signature.
{
and }
indicate the beginning and the end of a method. They define the method’s body and scope. This is where the actual logic is declared. If the method has declared a return_type
, the method body must include a return statement. This statement is the keyword return
followed by the variable that must be returned when the method is done running. The datatype being returned must match the datatype declared on the return_type
If the method was marked as void, it should not have a return statement.
Enough theory. Let’s look at some real code. Recall our Employee
class:
public class Employee { String firstName; public Integer startYear {get; private set;} Employee(String firstName, Integer startYear){ this.firstName = firstName; this.startYear = startYear; } }
And the code that was instantiating the objects:
public class EmployeeController { Integer currentYear = 2023; Employee jerryEmployee = new Employee('Jerry', 2015); Integer jerryTenure = currentYear - jerryEmployee.startYear; Employee elaineEmployee = new Employee('Elaine', 2018); Integer elaineTenure = currentYear - elaineEmployee.startYear; }
Instead of this spaghetti code, let’s refactor it.
We’ll move the employeeTenure
calculation into a method named calculateTenure
that accepts an Employee
object as a parameter. In the body, we’ll apply the formula and then return the result as an Integer called tenure
. This method will be called each time an employee’s tenure must be calculated:
public class EmployeeController { Integer currentYear = 2023; Employee jerryEmployee = new Employee('Jerry', 2015); Integer jerryTenure = calculateTenure(jerryEmployee); Employee elaineEmployee = new Employee('Elaine', 2018); Integer elaineTenure = calculateTenure(elaineEmployee); private Integer calculateTenure(Employee emp){ Integer tenure = this.currentYear - emp.startYear; return tenure; } }
Great!
The method is called using the calculateTenure(...)
syntax. Each instance of the Employee
class is passed in as an argument.
The method accepts the Employee
object as a parameter and locally uses the variable name emp
to keep track of it. emp
is a reference to the exact same object that was passed in. So if we passed in jerryEmployee
as an argument, we can use the parameter emp
to access all the data inside jerryEmployee
.
In the method body, each object's member variable is accessed via the dot operator to perform the calculation. The result is stored in tenure
. The return
keyword passes tenure
back to the original caller.
This is great because our code is a bit more DRY now. If the formula ever changes, we’ll just update the calculateTenure
method.
This is where scope really starts to matter. Scope determines the accessibility of a variable. The variable tenure
has a scope limited to the method body where it was declared. It cannot be accessed outside of that method, which is why we depend on the method's return type to convey the information.
The scope of currentYear
belongs to the entire class. So, everything within the class can access this variable. This means we can’t declare a variable named currentYear
in the calculateTenure
method. The class’s scope encapsulates the method’s scope.
Up to this point, we haven't been working with classes and methods, so we didn't need to concern ourselves with scope. However, moving forward, it will become an important consideration.