As we mentioned in the previous section, if we want runtime polymorphism, using casts can lead to unclean code.
By way of example, let's change our main.jspp code so that all our animals are inside an array. From there, we will loop over the array to render the animal. Open main.jspp and change the code to:
import Animals;
Animal[] animals = [
new Cat("Kitty"),
new Cat("Kat"),
new Dog("Fido"),
new Panda(),
new Rhino()
];
foreach(Animal animal in animals) {
if (animal instanceof Cat) {
((Cat) animal).render();
}
else if (animal instanceof Dog) {
((Dog) animal).render();
}
else {
animal.render();
}
}
Now our code is even less elegant than our original code that just instantiated the animals, specified the most specific type, and called render().
However, this code can be massively simplified until it becomes elegant. In fact, we can reduce the 'foreach' loop down to one statement. The answer: virtual methods.
Virtual methods enable "late binding." In other words, the specific method to call is resolved at runtime instead of compile time. We don't need all the 'instanceof' checks, all the casts, and all the 'if' statements as we saw in the code above. We can achieve something much more elegant.
First, open Animal.jspp and change the 'render' method to include the 'virtual' modifier:
external $;
module Animals
{
class Animal
{
protected var $element;
protected Animal(string iconClassName) {
string elementHTML = makeElementHTML(iconClassName);
$element = $(elementHTML);
}
public virtual void render() {
$("#content").append($element);
}
private string makeElementHTML(string iconClassName) {
string result = '<div class="animal">';
result += '<i class="icofont ' + iconClassName + '"></i>';
result += "</div>";
return result;
}
}
}
Save Animal.jspp. That's the only change we need to make.
However, just making our method virtual isn't enough. In Cat.jspp and Dog.jspp, we are using the 'overwrite' modifier on their 'render' methods. The 'overwrite' modifier specifies compile-time resolution. We want runtime resolution. All we have to do is change Cat.jspp and Dog.jspp to use the 'override' modifier instead of the 'overwrite' modifier. For the sake of brevity, I will only show the change to Cat.jspp but you need to make the change to Dog.jspp as well:
external $;
module Animals
{
class Cat : Animal
{
string _name;
Cat(string name) {
super("icofont-animal-cat");
_name = name;
}
override void render() {
$element.attr("title", _name);
super.render();
}
}
}
That's it. All we had to do was change modifiers. Now we can finally edit main.jspp so there is only one statement inside the loop:
import Animals;
Animal[] animals = [
new Cat("Kitty"),
new Cat("Kat"),
new Dog("Fido"),
new Panda(),
new Rhino()
];
foreach(Animal animal in animals) {
animal.render();
}
Compile your code and open index.html. Everything should work. Now we've been able to massively simplify our code and still get the expected behavior. Specifically, we reduced the code of our 'foreach' loop down from:
foreach(Animal animal in animals) {
if (animal instanceof Cat) {
((Cat) animal).render();
}
else if (animal instanceof Dog) {
((Dog) animal).render();
}
else {
animal.render();
}
}
To this:
foreach(Animal animal in animals) {
animal.render();
}
The reason we've been able to simplify our code so dramatically is because marking a method as 'virtual' signifies potential runtime polymorphism. Together with the 'override' modifier, the compiler knows we want late binding on the 'render' method so the "late" binding happens exactly when it's needed: the 'render' method will be resolved at runtime if and only when it needs to be resolved (inside the 'foreach' loop).
Similar Reads
Bootstrap 5 Introduction Bootstrap is a free and open-source collection of CSS and JavaScript/jQuery code used for creating dynamic websites layout and web applications. Bootstrap is one of the most popular front-end frameworks which has really a nice set of predefined CSS codes. Bootstrap uses different types of classes to
4 min read
Node.js Image Upload, Processing and Resizing using Sharp package Often in our web applications, we need to store multiple forms and formats of images, in the form of a profile picture or an image for a product in an e-commerce prototype. In most of our cases, we need to store images compressed to multiple sizes, retaining the quality. For example, for a product a
3 min read
IndexedDB | Introduction IndexedDB is a key-value database in the browser. It is a NoSQL Database. It is transactional, i.e. if a particular action falls within a transaction, none of the actions of that transaction is applied. This ensures the database remains consistent. Why use IndexedDB? The localStorage was designed fo
3 min read
JS++ | Static vs. Dynamic Polymorphism Static polymorphism is polymorphism that occurs at compile time, and dynamic polymorphism is polymorphism that occurs at runtime (during application execution). An aspect of static polymorphism is early binding. In early binding, the specific method to call is resolved at compile time. (JS++ also su
4 min read
JS++ | Subtype Polymorphism Subtyping describes type relationships, and subtype polymorphism enables operations defined for supertypes to be safely substituted with subtypes. Concretely, imagine the relation between a 'Cat' class and an 'Animal' class. (Remember: classes create data types in JS++.) In this case, within the con
3 min read
How to Scrape a Website Using Puppeteer in Node.js ? Puppeteer is a Node.js library that provides a high-level API to control headless Chrome or Chromium over the DevTools Protocol. It allows automating, testing, and scraping of web pages over a headless/headful browser. Installing Puppeteer: To use Puppeteer, you must have Node.js installed. Then, Pu
2 min read
JS++ Getters and Setters 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 cl
4 min read
JS++ | Types in JavaScript In this chapter, we're going to explore JavaScript programming styles and how developers worked with types in JavaScript (rather than JS++). This chapter will help you understand the next chapters which explain the JS++ type system in detail. In this tutorial, we will be using the Google Chrome web
10 min read
JS++ | Access Modifiers and 'super' Access modifiers allow us to change the "visibility" and "access privileges" of a class (or module) member. These are best understood by example. JS++ has three access modifiers: private, protected, and public. A private member is the least permissive. If a member is declared as 'private' it can onl
15 min read
JS++ | Upcasting and Downcasting Now that we understand both subtyping and static versus dynamic polymorphism, we can learn about upcasting and downcasting. Upcasting and downcasting is based on type relationships. In other words, if you have data of type 'Animal', you can "downcast" it to its subtype 'Dog'. Conversely, if you have
3 min read