When It Rained Fans & Shawarma, everybody gets a slice of Java (WIRFAS)
Praveen V Menon
I Listen, Communicate & Help in building complex tech things | Certified PSPO-1 | Product Owner | Agile Advocate | ML & AI Enthusiast | Author | Human
1-hour read
PREFACE:
This long-form article is only meant to be used as concept referencer of sorts, and has been created in the hopes of giving enthused people a different angle to the most important concepts of Java. These should then aid them while practicing Java Programming on their own. However, please note that I do not intend to clarify every single point relating to Java. Of course, if you have a specific question in mind I would give it my best shot to explain the same.
Mostly, this booklet will be free of syntax's and other kind of code flow; for all that we have Google. :)
PRESUMPTIONS:
The reader would be accustomed to some sort of programming, even if it's procedural programming.
GOLDEN RULES TO REMEMBER:
- Everything in Java is inside a Class. Be it functions, variables, objects, Cat, Dog, Fan, you name it.
- Almost every entity that you see in Java is an Object.
HOW TO MUNCH THIS BOOK:
1. If you are a visual person then each time we speak about an example Imagine the Object being mentioned and try picturing it on paper. We have left words here and there for every concept to help you visualize it.
If you are an auditory person then you could repeat the concepts to yourself and represent it in whichever way you feel best. E.g. - Charting down the Classes and instance variables as per their hierarchy while you say it out to yourself, would be a great option.
2. If there are some parts in the concept that you did not understand when reading, highlight it and move on to the next part. When you read some concept that either relates to this, or if you finish reading all the remaining pages you could give the highlighted parts a shot again. If it’s still all tangential, please do leave a comment and I’d give it my best to improvise on that a part a bit more.
3. Please also keep on writing programs based on questions from some credible books, based on each chapter you are progressing with this quick-reference book. The whole point behind this book is to give you an understanding of the concept if you are finding it hard to digest all the jargon in the book suggested by your school/college.
4. And, its okay to slurp. We’re actually banking on that :)
How does Java work?
We have seen a lot of code being written in our days of work and studies. For any programming language, the protocol is the same:
We use the required syntax's and implement the respective functions to get our code working the way we want.
However, the only requisite here in Java is that the source code file should be named after the class that holds the main function. So, if you are writing a code to let the world know which new year party you are going to, you would ideally have a class called Party and it would be as follows:
public class Party {
public static void main(String args[]) {
{
System.out.println(“I'm going TomorrowLand!!”);
}
}
The aforementioned code excerpt (although it doesn't deem fit enough to be called a program) should be named Party.java. Now coming to how javac (Java compiler) processes this:
- Party.java when compiled with javac would give you a Party.class file, which would be in byte-codes and won't exactly be very user-readable.
- This Party.class file when run in any machine with a JVM (Java Virtual Machine) would instantly print out your self-declaratory-statement of where to find you during the year end.
Please do not be misguided by thinking that every source file in Java should contain only one class. There is NO such rule. In fact, you could even have a class inside a class (inner class, but we are quite far from that). All that said, your source file should be named after your main class in that program.
If you ask me what does a "main class" refer to, well, I would say it is the one that holds the main function, i.e. main (). Simple?
I realize it's going to be considerably more easier if we are all on the same page with different terminologies that are used actively in Java. This would aid both of us in grasping a concept on mutually safe grounds. It's possible that I may have missed some terms, if so, please do leave that as a comment and I will try my level best to incorporate the same in here as well.
JVM or Java Virtual Machine
(The smartest things before smartphones)
Well, first things first, JVM is not a compiler. I say this explicitly because many people who are new to Java might soon come to presume that the often-referred-to-JVM is actually a compiler (May be you didn't, no offence).
So where does JVM come to help?
As was already mentioned formerly, Java has its own compiler which checks for syntax errors (javac) and converts the source code to byte-codes. This byte code is then handed over to the JVM which runs it and produces the required output. So in other words you could say JVM is the facilitator to bring-forth the necessary output.
Another important functionality of the javac is to restrain variables from holding data of the wrong type. Sadly, the JVM is a much more renowned figure in the Java circles, and the javac compiler is often overshadowed by it.
A typical Java Class
(Everything is a copy of a copy of a copy)
A class is a blueprint of an object.
Let's say you want to build your dream house. A two-storeyed structure with a lawn-terrace and a jacuzzi in an open-roofed bathroom.
This dream house is an object of your desire. You have all the things that you need in this house depicted clearly in your mind, but then again that won't be enough to get the house built. So, you get an architect, chit-chat with him and aid him/her to transfer your mental plan to notes and diagrams. A couple of weeks down your architect gets back to you with the blueprint of your dream house. You love your house but suddenly an idea strikes you!
You realize that such a house, once built, would sell off like hot cakes in the real estate market. Now since you really have quite some cash to roll, you then decide it would be great to have a living society with this plan, where all houses are similarly designed. You add some extra features to this plan and pass over this idea to some builders along with your investments.
So, what has this single blueprint of your house turned out to be? It has become a reference for all the future houses that are going to be built in your upcoming living society.
So OOPedly speaking, we now have a class DreamHouse which is composed of smaller objects such as Fans, Jacuzzi, TerracedLawn, Bedroom, etc. We can now create as many objects of class House to construct our society, and each of these objects would be a lawn-terraced, jacuzzi inbuilt Houses.
So then, what is an Object?
An object is an entity of type class.
Okay, I’m not trying to run around in loops with this, but this is the simplest way to define an OOPS Object. Nevertheless, I'll try to explain this a bit more elaborately.
Whenever we create an object of any Class, we are actually recreating that same Class and giving it an identity.
It’s like a chocolate muffin-making machine making a standard muffin, that it’s used to chugging out every day. We could thus say that an Object is actually an instance of a class.
If the example is still not clear, let’s refer to the former example of house, blueprints and society. I hope we can rightly say that the blueprint your architect made you for your dream house can be considered a Class. Now let’s assume that this Class was named DreamHouse.
So simply put, every house that you build in the future with the same blueprint is actually an object of type DreamHouse. We don't need to recreate a new blueprint, as that is already in place.
Instance Variables
An instance variable is something that an object knows.
Let me take the example of a Class called Fan to clarify what instance variables are.
We all know what a ceiling Fan consists of.
a. It has Blades, which actually create the wind action,
b. It has a Motor which is responsible for turning around the fan,
c. It has a Regulator that syncs up with the external wall/remote control and controls the motor accordingly to run at different speeds.
So, now if I tell you that the instance variables of a Fan are the things that it knows, i.e. Blades, Motor, Regulator, would it be too abstract?
Come to think of it, every object knows a few things, few things which have a value and that need to be included in the class to further simplify the latter's working. Not clear yet? I'll take another simpler example, but please reread the 2nd Golden rule of Java before that.
Let's get slurpy, let's think about a Shawarma! Now what all items do you need to make a yum shawarma? You need shredded chicken, mayonaisse, cucumber, tomatoes, onions and kuboos. Now things would become a lot easier if I say Shawarma is a Class. But a shawarma knows that it needs chicken, mayo, cucumber, etc. to come to it's true form. Thus, I hope we can rightly say those are the instance variables.
An object or instance is nothing without its instance variables. Instance variables define the state of the class. If the ingredients are mixed proportionately, the shawarma is bound to be awesome. (I would like mine with some extra mayonaisse :D)
Now if you are thinking why these are called Instance Variables and not Ingredients, Components, or even simply, Variables for God's sake, let me remind you once again that a class is “just” a blueprint of an object. So, after you have created the class Shawarma, every Shawarma that you make and serve your friends is an object or instance of type Shawarma. So then what else could one call Variables of an Instance other than instance variables?
Would it be wrong now to ask what Variables are?
Absolutely not, maybe I should have just clarified it long back. Variables can be considered to be containers. E.g.-
a. We have tequila shot glasses (very small)
b. Glasses used for serving Coke to guests (a bit bigger)
c. Coffee mugs (considerably big), and
d. Beer mugs (now those are real big ones).
Similarly, we have variables such as boolean, byte, short, int, long, float, double, char which are again varying in size and can accommodate different values of respective substances (types).
Basically, Variables are anything that holds values for a purpose, short term or long term.
The aforementioned variables are generically called Primitive variables. We have another type of variables called the Object Reference variables; pretty simple to guess that these are reference variables to objects. (Ha! You are getting a hang of me).
One more thing that must be known about variables is that a smaller variable cannot hold the contents of a larger variable. Naturally, how could a beer mug be emptied into a shot glass without spilling? Fortunately in Java, we won’t have such a spillage because this is another functionality of the compiler. The compiler will not let you assign a bigger variable to a smaller one.
Instance methods
Now since I'm already done with my Shawarma (Burp!), I need to get some breeze and so, I will return to my Fan class.
We all know what a fan does. It speeds up and slows down according to the controls that we employ on the regulator. That is exactly what our Class Fan does as well. It has functions or methods called SpeedUp() and SlowDown() defined as a part of its class structure.
What is defined inside these functions is solely up to the programmer of Class Fan. Now I hope I need not explain why they are called instance methods again. A Method of an instance does things. Thus, Instance Methods.
Briefly put, instance methods helps us to get a class to do something it can do.
So, what is the syntax for invoking an instance method of a class you ask? It is done with a dot relationship, the syntax for the same has been shown below for ready reference:
Object_name.instance_method_name()
Creating Objects
(a Class without an Object is like a man without a conduct, unusable)
I'm going to depart from my stand of not explaining syntax, as this is quite important.
Well, the syntax for creating a new object of type Shawarma is as follows:
Shawarma S = new Shawarma();
It is important to subdivide the above statement to 3 different parts:
(1) Shawarma s
(2) new Shawarma(), and
(3) =
So why do you think I bifurcated a simple statement? Simply because they are actually 3 separate processes.
Now let's consider each of them individually to understand clearly. However, to understand this we have to first reach a snack shop.
You go to a shop. Place an order for a shawarma to the waiter. The waiter passes on the order to the cook. The cook along with other orders makes one just for you. He then passes it on to the bearer and you receive it ready to be snacked upon.
So what actually happened here? There was an order, the cook makes it for you and the order is connected to the dish. This is exactly what happens in Java as well when we create an object with the above syntax.
In (1), you are placing an order for an object of type Shawarma.
In (2), the JVM creates the requested object and places it on the heap, and
In (3), your order is connected to the object on the heap.
If you ask me what is 'S' in the above example I would say it's an Object Reference variable. Why is it called an object reference variable? Because it holds the reference bits that represent a way to access an object. It doesn't hold the object itself, but holds the address (or something not readable to us lowly humans). The limitation in Java is that we can't confirm what is held by a reference variable. We only know that it represents the access to one and only one object, and the JVM knows how to use it.
For now, simply let's assume 'S' to be a remote control that can be tuned to refer to some particular object.
Please, let's not get to the exact size of reference variables part. We know now that these variables are somewhat like pointers and hold the address like pointers. They could be 64 bits or whatever, depending on the compiler.
Object Allocation happens dynamically in Java
Now the owner of this snack shop is a wise man. He had an object-oriented mind, which knew the best way to handle any kind of crowd was to serialize the dishes!
He created colored, numbered tokens for each kind of dish:
? Orange for Shawarma
? White for LimeJuice
? Green for GrilledChicken, etc.
Result, a waiter would take the orders and give a customer these numbered tokens. He would then just need to pass on the order to the cook, who after preparing the food would pass it on to a bearer, who would deliver the food as per the token colors. This enabled them to serve any number of consumers with the same number of employees. Hope you got the metaphor relating this with Java object handling methodology.
The advantage was that there were next to nil misplaced orders, which means somebody who asked for a Shawarma would not end up getting a LimeJuice. Although delivering a wrong dish might not seem to have a lot of implications, let's see how it can actually matter quite a lot:
Let's assume the snack shop has a new customer who is visually impaired. And suppose the bearer misplaces the order and delivers him GrilledChicken when he actually ordered for a Shawarma. What would then happen? Ideally Class LimeJuice would have an instance method called DrinkUp(), while Class Shawarma would have one called ChompDown().
Shawarma s = new LimeJuice; /*the case when the bearer delivers a LimeJuice instead of Shawarma */
s.ChompDown(); /*this has got to be wrong, how can you chomp down a glass of Lime Juice, unless you are a troll from Harry Potter */
Java cares about type. It will not allow you to stuff a Shawarma Variable into a LimeJuice object, or vice versa.
We talked about object creation, what about object deletion?
Object allocation happens on the heap dynamically like we mentioned before in the Shawarma store example. But what differentiates Java from C is that freeing of objects also happens dynamically here. We need not specifically delete or free it with user introspection. Java utilizes what is called the Garbage Collector or more predominantly, GC.
A garbage, as we know, literally means anything that we don’t use anymore. Similarly, in Java any object that is not being used anymore is garbage to the JVM, and it soon utilizes the GC to free that memory.
How does this process happen you ask?
Remember we said that objects created on the heap are only referred to by their object reference variables? To reiterate, these object reference variables are the only thread that connects the object on the heap to the user, or source code. So, if the object reference variable that used to point to a particular object on the heap is re-assigned by the user to refer to another object on the heap (or is assigned NULL), what do you think happens?
The old connection is lost leading to the only thread also being dissolved, leaving the object stranded on the heap, alone and unwanted. The JVM recognizes such stranded objects and makes it eligible for Garbage Collection.
The OOPs edge
(Because nobody is fond of oops moments in life!)
It so happened that when I published my DreamHouse housing society project on the newspaper, I was contacted by one South Indian filmstar Santhosh Pandit. This man has a flare for generally exaggerating things and thus he requested me that the Fans in his House should be state-of-the-art fans. On further trudging he said that the fans should automatically control their speed according to the temperature of the room, and should not need him to move around his lazy a** to modulate the regulator. I knew this wasn't a big deal and could be easily be worked out. Thanks to OOPs.
I was soon informed by one of my friends that Mr. Pandit had also approached one of our competitors with the same demand, but was sadly turned down. It so happened that my competitor was depending on Procedural Programming which would need the complete Fan function to be redefined and re-implemented. How lousy, right? As for me, all I had to do was improvise my Class Fan a little to suit Mr. Pandit's needs. Let me tell you how the self-controlling-fan was created simply.
My initial Fan Class looked like this:
public class Fan {
int Blades; //number of blades
long Motor; //power of motor in kW
String Regulator; //brand of regulator company
public void SpeedUp() {
//some random calculations to step up the motor
}
public void SlowDown() {
//some random calculations to step down the motor
}
}
Now, because of Mr. Pandit I had to incorporate a temperature Sensor in my Regulator. The status of the temperature sensor was then checked at regular intervals of time. If the Sensor showed a temperature higher than the average room temperature it would call the SpeedUp(), otherwise it would call the SlowDown().
So, after the above spicing, now my Fan running program would look somewhat as follows:
public class Fan {
int Blades; // number of blades
long Motor; // power of motor in kW
String Regulator; // brand of regulator company
static int AvgRoomtemp = 24; // average room temperature -> NEW
int Sensor; // temperature read off the sensor -> NEW
public void SpeedUp() {
//some random calculations to step up the motor
}
public void SlowDown() {
//some random calculations to step down the motor
}
public void CheckSensor() { // NEW
if (Sensor > AvgRoomTemp) {
SpeedUp();
}
else if (Sensor < AvgRoomTemp) {
SlowDown();
}
}
public class RunFan{
public static void main(String args[]) {
//call CheckSensor at regular interval of time
}
}
I did not even need to touch my old-tested code and yet was able to serve my purpose. My company would have been cursing our darned luck and pointing fingers at each other if we had adopted Procedural Programming like our competitor Shashi Builders.
So now you see how tweaking my code a bit allowed me to cater to my customer and even charge him a considerable amount for the same. What's more, this sale also gave me more mileage in the market as many more customers were readily waiting to book a house to at least get a glimpse of the superstar before they start their respective days. They said that even a small meeting with the superstar relieved them of a lot of their frustrations. God and Mr. Pandit only knows what they meant.
Arrays in Java
(because being organized is everything, literally)
Thankfully, arrays won’t be something hard to understand in Java, thanks to the concept of Object reference variables that we are already thorough with.
(1) int [] nums = new int[7];
(2) Fans [] f = new Fans[7]
You know what is happening here right, pretty simple syntax stuff. We are creating an array of 7 elements of type Integer in (1), and of type Fans in (2).
(1) is fine, as we know that array would contain 7 integers whenever they are assigned by the users, e.g.:
nums[2] = 43;
or,
nums[6] = 786;
But, what would we assign to the array in (2)?
If you guessed this one straight then you are positively a certified OOPer. As already mentioned they have been designed to hold objects of type Fans, but since the object Fans cannot be quantified like an integer or string, how else could a user possibly assign objects to it, other than as follows:
f[1] = new Fans();
f[5] = new Fans();
So basically, every cell of the array of type Fans would hold the references to actual objects on the heap.
I.e. f[1] is the remote to control a particular object on the heap, while the array f itself is a remote to control f[1].
There's a rumor that there is something better than Arrays in Java, true?
I don't know where your sources are from, but they are spot on correct!
We know that Arrays can store any kind of variables in it, be it primitive or object reference type. All we need to do is to declare it accordingly. However, what are the downsides of using Arrays in spite of their ability to serialize and compartmentalize data?
This answer would come straight to you if you have ever needed to:
a. Find an element, or
b. Find the index of an element/cell, or even
c. Append the array.
In any of the above cases we have to resort to loops and other such lengthy stuff. This is where ArrayLists come to the rescue!
Following are some things you can do with ArrayLists:
add(objectElem); //adds the object instance to the Array
remove(int index); //removes an object at the said index
remove(objectElem); //removes this object from the ArrayList (if it's there)
contains(objectElem); //checks if the said object element exists in the ArrayList
IsEmpty(); //returns 'true' if the list is empty.
indexOf(objectElem); //returns the index of the object parameter, or -1 if nonexistent
get(int index); //returns the object the said index
size(); //returns the number of elements currently in the list
Too good to be true right? Don’t worry the syntax to define an ArrayList isn't complex either:
ArrayList<Shawarma> cookList = new Arraylist<Shawarma>();
Shawarma s = new Shawarma();
cookList.add(s); //as simple as that
Here cookList is simply an ArrayList of Shawarma objects.
Alternatively, you could also see ArrayList as an Object, which 'knows' objectElem and index (instance variables) and 'does' add(), remove(),contains(), etc. (instance methods).
Encapsulation
(because mistakes happen when precautions aren't taken seriously)
Surely, all of you have seen the latest iPhone phones, the ones that come with a fingerprint scanner. What are the reasons Apple introduced a fingerprint scanner on it's phones, according to you? Let me call out the obvious ones
- Because your phone is private to you!
- It knows almost everything about you as a person.
- Why would you allow such a private substance to be publicly accessed?
Thus, adding a fingerprint scanner to their phones allowed Apple to modify the access rights of everyone. But I won't give the full credit of this concept to Apple, this has been said in OOP ages ago.
As we said before:
- The instance variables of a class know everything about it
- It's instance methods does all the things that it is expected to do when any created instance variables access it with the help of a dot operator.
But doesn't this mean that any instance variable could uses the dot operator to have any object to do what they are best at.
It’s almost like anybody who knows your lock code or pattern of your phone could do a lot of things with it. Which is why fingerprint scanners came into play.
So, since we can’t bring such contractions inside a program, Java has yet another simple methodology for the same, Encapsulation.
Marking all your instance variables as private, so that they can only be accessed by some instance methods of that class is basically what Encapsulation is about.
This step would ensure that any random object of the class cannot access the private variable and modify them with the help of a dot operator.
The next step to complete the process of controlled access is to make Getter and Setter methods for each individual instance variable. This would compel everyone to go via the Setter method to change this method, and guarantee prevention of unwarranted changes to the important parameters of an Object.
E.g.- The Setter method, if used in our Fans object from the DreamHouse class), could use conditions that:
- wouldn't allow NULL values to be assigned to NumberOfBlades, or
- Some negative value to be assigned to AvgRoomTemperature.
Please note that Instance variables are provided a default value when they are declared. They need not be initialized by Setter methods or even assignment operator before they are Getted.
Integers and Float variables have a default value of 0 and 0.0 respectively,
Booleans and Reference variables are provided False and NULL as default values when declared as an instance variable.
However local variables (the ones declared inside an Instance Method) should be initialized before use.
The Java Libraries
(are generically very renowned and knows how to handle this stardom with 'class')
Java has taken the hard way of evolving from a cycle to a car so it knows the things that programmers need generically. It has thus not only defined these functionalities before-handed, it has also earned a programmer the liberty to walk in at 10AM and leave by 5PM. [P.S.: Don't take the latter statement for granted]. Java has a library with hundreds of pre-built classes and t's popularly known as the Java API.
The Java API provides the programmer with classes that need not be re-written.
These classes are grouped into packages. All one needs to do is import the appropriate package with the class and use the corresponding functionality.
e.g. - String, Math, even the System.out.println are actually Classes defined in java.lang package, and each time we instantiate these we are just making an object of it. (Recollect the 2nd Golden Point of WIRCAS?).
It's just like the foods from various shops might have its special dish, e.g.- Cafe Arabia might make awesome Shawarmas, Hotel Malaya might offer the best Noodles, Aaryas Restaurant might have the most delicious South Indian Meals. So, if we were to suggest to a friend on the best food to have, we would naturally say the name of the shop along with the dish as only then can our friend zero in on the perfect match.
Similarly in the case of Libraries, it’s not just enough to use the Library name but instead specify the exact package we need to use in our code. E.g. - If we need to use ArrayLists, we need to tell Java “import java.util.ArrayList” before actually using it in our code.
The only hard thing about the Java API is knowing how to use, like any other new technology that you are introduced to. Basically, looking it up as and when you need it, and leverage it. However, this can be easily Googled as per the particular need for your code.
Inheritance
(Because what is a code that cannot be personalized as per individual needs)
We have already explained that in OOPs we can compartmentalize modules and write unique code. But, what if we want to use some code that has already been written and then append it somehow for the current cause at hand?
With Inheritance we have the provision to "extend" an already existing code to suit our needs, without touching the existent, working code.
Let's take an example to simplify things:
Most of us are a fan of Subway Sandwiches. For those who haven't felt the need to munch into this delicacy, let me explain.
We can customize the sandwich as per our need, i.e. select the Bread, Veggies, and Sauces along with the main item of our choice.
E.g. - Ideally I would go for a ChickenTeriyaki Sandwich :
- Choose a MultiGrain Bread
- Select Lettuce, Cucumber, Tomato and Jalapenos as Veggies, and
- Mayonaisse, RedChilli, SouthwestChipotle and Barbeque Sauce as Sauces.
In this case, the "SubwaySandwich" class could look like this:
public class SubwaySandwich {
string Bread;
boolean Cheese;
boolean Toast;
string [] Veggies = new String [5];
string [] Sauce = new String [5];
public void getUserChoice(SubwaySandwich s) {
s.getBreadChoice();
s.CheeseNtoast();
s.chooseVeggies();
s.SelectSauce;
public void getBreadChoice() {
/* statements to get the user choice of bread and assign it to Bread*/
}
public void CheeseNtoast() {
/* statements to ask the user if cheese is needed or not*/
}
public void chooseVeggies() {
/* statements to get the user choice of veggies and assign it to Veggies[]*/
}
public void SelectSauce() {
/* statements to get the user choice of sauce and assign it to Sauce[]*/
}
}
public class ChickenTeriyaki extends SubwaySandwich {
int quantity; //quantity of chicken teriyaki
public void makeSandwich() {
/* statements to make the sandwich*/
}
}
public class SelectSandwich {
public static void main(String args[]) {
ChickenTeriyaki ct1 = new ChickenTeriyaki();
ct1.getUserChoice(ct1);
ct1.makeSandwich();
}
}
Although the program above is not perfect in few terms, I hope we can understand how we could use Inheritance to extend our code. Let’s break this up for ready reference:
- SubwaySandwich already has a set of variables and functions that are common to all subway sandwiches, i.e. Breads, Veggies, Sauces, getUserChoice and makeSandwich, etc.
- To make a new sandwich all we need to do is just inherit the already existing things (i.e. Bread, Veggies and Sauce), and add only what is needed as per the user’s selection of the Sub. In this case all that needed to be added was the Chicken Teriyaki's quantity. Apart from that all is common and ready to be served as per user selection.
The SubwaySandwich class in this case can be referred to as a superclass, and the ChickenTeriyaki which extends it is called the subclass.
The superclass can be inherited across any number of subclasses, however, the only thing that needs to be ensured is that the subclass is a type of the superclass. E.g.:
- ChickenTeriyaki is a SubwaySandwich,
- Dog is an Animal,
- Shawarma is a Snack,
- Fan is an ElectronicComponent.
In these examples, ChickenTeriyaki, Dog, Shawarma and Fan are subclasses while SubwaySandwich, Snack, Animal and ElectronicComponent are superclasses.
Please know that any class can be a superclass and anything can be its subclass.
All we need to check is if it satisfies the IS-A relationship.
E.g. ChickenTeriyaki IS-A SubwaySandwich, and SubwaySandwich IS-A Snack , Snack IS-A Food and so on.
In the aforementioned examples, SubwaySandwich is a superclass for ChickenTeriyaki, but is a subclass for Snack (assuming Snack is another Class), and Snack is another subclass for the class Food.
So, as you can see the inheritance hierarchy train has no limit, it could go on and on until the IS-A relation is satisfied.
Overriding functions in Inheritance
Let us define the above mentioned class Food.
public class Food {
String plate;
public void EatItUp() {
/* statements for eating up your food*/
}
}
Now if you come to think of all the classes that could possibly extend this, we could think of quite a few.
We have already seen Class Snacks, we could further have classes like Starters, FullCourseMeals and such other which IS-A type of Food. But although all these Food need to be eaten up, they might all be consumed in different ways, right?
So would you think it would be possible to define the method EatItUp() in a generic way.?
E.g. - Some would need to be had with chopsticks, while some like soup would need a spoon and our SubwaySandwich would definitely need bareHands.
So, what do we do in a situation when we would need to customize the already existent definition of an instance method?
Java ensures that even though your superclass has defined a particular method in a generic way, you could actually override that method by redefining it in the subclass. Simply use the keyword override in the subclass definition.
Please note that we shouldn't use inheritance for code re-usability, it’s only to be used if the subclass “IS-A” type of superclass.
You can only extend one class, and if you feel the need to extend more than one then that might be because you haven’t yet finalized the main IS-A relationship.
E.g. – SubwaySandwich IS-A Snack and also IS-A Food. But that doesn’t mean it will try to inherit both the classes (Java will not let you). We then must realize that Snack also IS-A Food. Now suppose a new class called NonJunkSnack is created to demarcate Snacks. Let's break this up better readability
- So we know NonJunkSnack would extend Snack Class
- Snack Class in turn extends Food Class.
Now the choice to actually decide if SubwaySandwich Class should extend the Snack Class or the NonJunkSnack Class is the programmer's choice.
But it can’t inherit both! That’s the bottom-line.
Extending an abstract class
As the definition of the term abstract goes, “existing in thought or as an idea but not having a physical or concrete existence”, similarly, an abstract class is something that can NOT be instantiated.
We know what a Fan looks like, or a Shawarma, or a SubwaySandwich. We know what the components are that constitute it and thus, could use it as a blueprint for future objects.
But what about the class Food? We used it as a superclass in one of the above examples but we wouldn't know what kind of instance variables we could use for it. We could have the method called EatItUp(), which would be common to all its subclasses. But still there’s actually no point of creating an object of type Food, because it basically doesn’t do anything generically. It will not mostly have any instance variable, i.e. it would basically not know anything. So then could we rightly say that basically an abstract class is useless unless it’s extended?
Once you declare class as abstract the compiler will not allow you to instantiate an object of that type. You would however be able to create an object reference variable of that type, so you could easily apply polymorphism (yes, don’t worry we will talk about this as well).
Overriding an abstract method
Yes, and as the name suggests this is a method without a body/definition. If you are wondering what is the point of having a method that doesn't have a definition, understand that such methods are used when we need to maintain uniformity in method names, but still cannot have a generic definition to meet the needs of all subclasses that extend it.
E.g.- Class Food, which is extended by all subclasses, may have a method called eat(), but could we define it generically for all objects, i.e. Noodles need to be EatenWithFork(), while Omlette would need Knife&Spoon(), whereas Sandwiches would need UseBareHands(). Thus we can see although all these classes (Noodles, Omlette,, etc.) come under Food, we can’t actually define a common way to eat all these.
In such a case Eat() is declared as abstract in Food, and each time in the subclass we define it with the respective way it needs.
So, an abstract class needs to be extended, whereas an abstract method needs to be overridden.
Another thing that should be remembered is that if ANY method in a class has been declared abstract, then that class must be made abstract as well. But, every method of an abstract class need NOT be abstract.
But, what if there’s a definitive need for extending more than one class? Fine, implement it.
Going by the creative folks that we are, there might be a point where we need to make SubShawarma, what then?
By the looks of it, we surely need to extend the SubwaySandwich Class and Shawarma Class as well. But, you can’t extend both, as java does not support multiple inheritances.
Let us assume that in a SubShawarma, we would need to replace the Kuboos with SubBread, but the fillings would remain as of a Shawarma. Thus, we can clearly see that a hotch-potch of all the classes would be needed, and as such a redefinition. Overriding would have been an option, but thanks to the Deadly Diamond of Death caused by multiple inheritance, that option is also ruled out.
So, what could be done is we could change the definition of either or both classes to interface, and make all the methods in it abstract. In this way we would need to define each method before using it.
This would not only give us the flexibility to use the code as per our need, it would also enable us to implement the features of more than one class.
Polymorphism
(with great power comes great responsibility buddy)
Suppose we are writing a program that calculates the calories consumed after any meal. The following could be done:
Class Calories {
Public void Calculate (Food f) {
String [] ItemsInTheFood = new String [10];
for (int i=0; i<10; i++) {
ItemsIntheFood[i] = f.ingredients(); //to fetch the ingredients in that food and store it in the String Array accordingly.
}
// do things to calculate your calorie intake for each ingredient
}
}
Public class CalorieCalculator {
public static void main(String args[]) {
Calories c = new Calorie;
Shawarma s1=new Shawarma();
SubwaySandwich ss1=new SubwaySandwich();
c.Calculate(s1); //Shawarma’s Calorie calculation is initiated
c.Calculate(ss1); //SubwaySandwich’s Calorie calculation is initiated
}
}
In the above excerpt if you noticed the Food parameter used in the Calculate() accepts arguments of both object type, Shawarma and SubwaySandwich. On a high-level this is what Polymorphism symbolizes and offers.
I know this isn't the best example for anyone to make sense, so I wouldn't let this lay hanging and will explain further this God-mode feature of Java.
We saw that in Inheritance, if a subclass IS-A type of the superclass, the latter would obviously know all of its subclasses. Thus Classes higher up in the inheritance tree could naturally then accommodate ALL its subclasses equally.
Calculate() is ready to take up any object of type Food. And in the main() we are actually passing two object reference variables separately, of type Shawarma and SubwaySandwich respectively. Both these object reference variables refer to objects belonging to different classes respectively (I.e. Shawarma and SubwaySandwich), however what might have eluded you is that Shawarma IS-A Food and so IS SubwaySandwich.
Simply put, with Polymorphism, an object of the subclass can be referred to by an object reference variable of the superclass.
How does Polymorphism work?
For the sake of better understandablity, let's continue the same example that we were talking about before.
When the call for finding the ingredients on any Food type is passed in the argument, the object referred to on the heap is called for and the ingredients are returned for the same. We hope you still remember that any Object Reference Variables are just like remote controls to refer to an object on the heap. So this means that we could also do:
Food f2 = new Shawarma();
This isn’t wrong, in fact, it can give you more flexibility than you could imagine.
Food for thought, you could use a same reference variable to refer to different objects belonging to its subclass, and even call for functions that are held commonly in these classes. I.e.-
f2.eat();
This function could be called irrespective of whichever object f2 is referring to on the heap, provided the object on the heap has a function called eat() defined (either within its class or inherited from its superclass).
At this point of time I would like to remind you once again that an object reference variable is just a remote control to refer to an object on the heap, and not the actual object.
Object as a Guru
(because everything in Life has it's roots somewhere)
We had mentioned this in the Golden rules; we just thought a little more clarification of the same could be helpful.
Every Class in java extends Class Object.
We didn’t call this out explicitly till now but still, this is what has been happening all this while.
To be brief, Class Object can be called the superclass of everything. Irrespective of how we go in the inheritance tree, the top point is always Object Class.
Something like what we could call a God class.
----------------------------
Hope this helps! In case of any conceptual doubts, please feel free to leave a comment.
Would like to reiterate that one shouldn't refer completely to this concept-referral article for syntax's and program-flow. All the best :)