In our previous example, we defined a 'setName' method that sets a class 'name' field we declared. A method whose sole responsibility is to write to or modify a class field is known as a "setter" or "setter method." Conversely, a method whose sole responsibility is to return the current data of a class field is known as a "getter" or "getter method."
Containers like arrays have methods like 'length' which are getter methods:
int[] arr = [ 1, 2, 3 ]; arr.length; // 3
The 'length' method for arrays returns the size of the array, determined by the number of array elements. However, you may have noticed a peculiarity: we didn't have to use parentheses to call the 'length' method. This is because the 'length' property of arrays is defined in a special way. It turns out we can define our own classes to have such special methods.
Let's start by re-defining our 'setName' method as a setter method. Open Cat.jspp and simply add the 'property' keyword in front of 'setName' like so:
external $; module Animals { class Cat { string name = ""; var $element = $( """ <div class="animal"> <i class="icofont icofont-animal-cat"></i> </div> """ ); property void setName(string name) { this.name = name; } void render() { $("#content").append($element); $element.attr("title", name); } } }
At this point, if you try to compile, you'll get an error (JSPPE0150). We need to edit our main.jspp file to reflect our change to a setter. Since we defined 'setName' as a setter, we can no longer call it using parentheses but we must use the assignment (=) operator instead:
import Animals; Cat cat1 = new Cat(); // cat1.setName("Kitty"); cat1.setName = "Kitty"; cat1.render(); Cat cat2 = new Cat(); // cat2.setName("Kat"); cat2.setName = "Kat"; cat2.render();
Now, if you try to compile the project, it should be a success.
That was quite easy! The only detail we may want to consider at this point is that the name is a property of the cat. 'setName' implies an action. Since we cannot have fields and methods with conflicting names, we can rename our private 'name' field to any of the numerous naming conventions: mName, _name, etc. In this tutorial, we're going to prefer the underscore to avoid name clashes since, in some dynamic languages (including JavaScript and Python), underscores are used to "denote" privacy (even if they aren't really "private" in practice). By renaming our private 'name' field, this frees us up to use the identifier 'name' as a setter method. Change the Cat.jspp code as follows:
external $; module Animals { class Cat { string _name = ""; var $element = $( """ <div class="animal"> <i class="icofont icofont-animal-cat"></i> </div> """ ); property void name(string name) { _name = name; } void render() { $element.attr("title", _name); $("#content").append($element); } } }
Also, change main.jspp to reflect this change:
import Animals; Cat cat1 = new Cat(); cat1.name = "Kitty"; cat1.render(); Cat cat2 = new Cat(); cat2.name = "Kat"; cat2.render();
Setter methods allow us to assign a value (write operation). However, if we wanted to get the value (read operation), it won't be allowed unless we define an accompanying getter method. Try to "read" the 'name' field like so:
import Animals; Cat cat1 = new Cat(); cat1.name = "Kitty"; cat1.render(); Cat cat2 = new Cat(); cat2.name = "Kat"; cat2.name; cat2.render();
If you try to compile, you'll get an error:
[ ERROR ] JSPPE0203: No getter defined for `Animals.Cat.name' at line 8 char 0 at main.jspp
As you may have deduced by now, the parameter for a setter method is the value assigned to the property (the value on the right-hand side). In our main.jspp above, the values were "Kitty" and "Kat" strings. Naturally, setter methods are only allowed to accept one parameter. Since getters are a "read" operation, no values need to be accepted; thus, getter methods intuitively do not require parameters. Using this intuition, we can define an accompanying getter method:
external $; module Animals { class Cat { string _name = ""; var $element = $( """ <div class="animal"> <i class="icofont icofont-animal-cat"></i> </div> """ ); property string name() { return _name; } property void name(string name) { _name = name; } void render() { $element.attr("title", _name); $("#content").append($element); } } }
Now, if you try to compile the project, you should be able to successfully compile.
As you've seen, if you define a setter without a getter, you can prevent all "read" operations. In contrast, if you define a getter without a setter, you can prevent all "write" operations. If you define both, you can have both read and write operations. This allows you to customize to your needs.