In this article, we will learn about one of the most fundamental concepts of Java: Java Classes. This article will only cover Java Classes, not the Objects created from classes.
Table of Contents
1. Introduction
Java is an Object-Oriented Programming language, which means that everything in Java is made to reflect real-life objects and entities. For instance, a class named Car
would represent a car and a List
would represent a list of elements in the actual world.
2. Java Classes
Java Classes represent entities with some characteristics (fields) which are vital to the class. They can also perform actions (methods), providing functionalities to the class. Lastly, we can use those classes to create (constructors) as many Objects as we need.
Perhaps, it’s would be a better idea to see all that in an example. Let’s assume we wanted to make a Robot
. Our Robot
would have a name
, a primary color
for its body and a preferred language
to speak. Also, the Robot
would be able to introduce themselves, greet us, and perform a simple math operation.
2.1 Defining Our Robot Class
Firstly, let’s create the Robot
class. In Java, there can be only one public class, whose name must be the name as the name of the file. We can have as many non-public classes as we want. Additionally, by convention, class names start with a capital letter. As a result, this file would be named Robot.java
. So far, we have the following code:
// Declared in Robot.java public class Robot { }
As you can see, we create Java Classes with the class
keyword. The access modifier of the class would impact its usability by other classes; a stricter access modifier would mean that the class could be used by fewer classes. In our case, a public
class can be used by any other class that exists in Java.
2.2 Creating a Robot Object
Java Classes need to be constructed in order to be used. By constructing a class, we create a new object from the class which can be used in our programs. The construction process can include the initialization of a few characteristics (fields) of the class.
Right now, despite the Robot
class being essentially empty, we can still create a Robot
object, thanks to the default constructor that all classes have. This constructor takes no arguments and would initialize all fields with their default values. If we included any constructor, we would not have that constructor but we could define it manually as such (inside the Robot
class):
// Declared in Robot.java public class Robot { // default constructor if we do not define any other constructors; otherwise we would have to create it on our own public Robot() { } // a custom-made constructor public Robot(String name, String color, String language) { } }
We are going to use the second constructor in the next section.
2.3 Giving Identity to Our Robot Class Through Class Fields
Moving on, we can begin adding the characteristics we mentioned for our Robot
. Any Robot
has the following three characteristics:
- A
name
that describes the name of theRobot
and differentiates it from other robots. - A
color
representing its main body color. - Lastly, a
language
for when it performs actions.
Let’s add those to the code, as such:
// Declared in Robot.java public class Robot { enum RobotLanguages { ENGLISH, SPANISH, GREEK } enum RobotColors { BLACK, WHITE, RED, BLUE, GREEN, YELLOW, PURPLE, PINK, ORANGE, BROWN, GRAY } // fields; the characteristics of a robot String name; RobotColors color; RobotLanguages language; // default constructor if we do not define any other constructors; otherwise we would have to create it on our own public Robot() { } // a custom-made constructor public Robot(String name, RobotColors color, RobotLanguages language) { this.name = name; this.color = color; this.language = language; } }
Let’s see what we have added:
- Line 4: We added an enum for all supported languages of a
Robot
. By using enums, we make sure that we cannot pass an invalid language that theRobot
cannot understand. For instance, if the language was aString
, we could pass any randomString
as the robot’s language, which is not valid. - Line 8: By following the same logic for the robot’s language, we define the robot’s color as an enum as well.
- Line 13-15: We define the main characteristics of a
Robot
as fields. EveryRobot
will have these fields from now on. - Line 23-27: By using this constructor, we can create a
Robot
object with specific characteristics, by initialising all fields of theRobot
class. The keywordthis
is used to refer to a variable that belongs to the class, rather than a method. For example,this.name
referes to the fieldname
of theRobot
class, while thename
after the “=” refers to thename
that is passed as an argument to the constructor. We could avoid using thethis
keyword, if the argument had a different name that the field, like so:
public Robot(String n, String color, RobotLanguage language) { name = n; this.color = color; this.language = language; }
2.4 Providing Functionality to Our Robot Through Class Methods
Lastly, it’s time to give some life to our robots, by setting up a few methods inside the Robot
class:
- Firstly, an
introduce()
method, which announces the robot’sname
andcolor
in the robot’slanguage
. - An
setLanguage(RobotLanguages newLanguage)
method that updates the robot’s spokenlanguage
tonewLanguage
. - Lastly, a
calculate(double operand1, double operand2, RobotOperations operation)
that performs the arithmeticoperation
between the two operands and announces the result. We are going to create another enum namedRobotOperation
for this.
Let’s add all these into our class, one by one.
2.4.1 The introduce() Method
Starting with the first method we want to add, we have to take into account the 3 different languages the Robot
can speak. So, every time a Robot
talks, we have to cover the same phrase in all languages. So, we will create two private
methods that will help introduce
translate the color
and language
fields. The reason they are private
is that those two methods are only useful inside the Robot
class, since they are helper functions, so we hide them from other classes. This is another OOP concept called Abstraction.
The introduce()
method is the following:
public void introduce() { switch (this.language) { case GREEK: System.out.println("Γειά σας! Με λένε "+ name +" και έχω " + this.translateColor() + " χρώμα.\nΜιλάω " + this.translateLanguage() + "."); break; case ENGLISH: System.out.println("Hello there! My name is "+ name +" and my color is " + this.translateColor() + ".\nI speak " + this.translateLanguage() + "."); break; case SPANISH: System.out.println("Hola! Me llamo "+ name +" y soy " + this.translateColor() + ".\nHablo " + this.translateLanguage() + "."); break; } }
According to the robot’s language, the introduction is made in a different language. The helper functions are translateColor()
and translateLanguage()
. Both of the methods return a String
of either color
or language
in the correct language. Starting with translateColor()
:
private String translateColor() { String translatedColor = this.color.toString(); switch (this.color) { case RED: switch (this.language) { case GREEK: translatedColor = "κόκκινο"; break; case ENGLISH: translatedColor = "red"; break; case SPANISH: translatedColor = "rojo"; break; } break; case BLUE: switch (this.language) { case GREEK: translatedColor = "μπλε"; break; case ENGLISH: translatedColor = "blue"; break; case SPANISH: translatedColor = "azul"; break; } break; case GRAY: switch (this.language) { case GREEK: translatedColor = "γκρι"; break; case ENGLISH: translatedColor = "gray"; break; case SPANISH: translatedColor = "gris"; break; } break; case PINK: switch (this.language) { case GREEK: translatedColor = "ροζ"; break; case ENGLISH: translatedColor = "pink"; break; case SPANISH: translatedColor = "rosado"; break; } break; case BLACK: switch (this.language) { case GREEK: translatedColor = "μαύρο"; break; case ENGLISH: translatedColor = "black"; break; case SPANISH: translatedColor = "negro"; break; } break; case BROWN: switch (this.language) { case GREEK: translatedColor = "καφέ"; break; case ENGLISH: translatedColor = "brown"; break; case SPANISH: translatedColor = "marrón"; break; } break; case GREEN: switch (this.language) { case GREEK: translatedColor = "πράσινο"; break; case ENGLISH: translatedColor = "green"; break; case SPANISH: translatedColor = "verde"; break; } break; case WHITE: switch (this.language) { case GREEK: translatedColor = "άσπρο"; break; case ENGLISH: translatedColor = "white"; break; case SPANISH: translatedColor = "blanco"; break; } break; case ORANGE: switch (this.language) { case GREEK: translatedColor = "πορτοκαλί"; break; case ENGLISH: translatedColor = "orange"; break; case SPANISH: translatedColor = "naranja"; break; } break; case PURPLE: switch (this.language) { case GREEK: translatedColor = "μωβ"; break; case ENGLISH: translatedColor = "purple"; break; case SPANISH: translatedColor = "morado"; break; } break; case YELLOW: switch (this.language) { case GREEK: translatedColor = "κίτρινο"; break; case ENGLISH: translatedColor = "yellow"; break; case SPANISH: translatedColor = "amarillo"; break; } break; } return translatedColor; }
Despite looking overwhelming due to its size, translateColor()
is quite simple. Depending on the robot’s color
and language
, the method returns the appropriate word for that color in that language. Due to the fact that there are 3 languages and 11 colors, there are 33 cases, which explains the size of that method. And, because we are combining switch statements with enums, we can be sure that we do not forget any enum value.
Similarly, albeit smaller, translateLanguage()
is defined as such:
private String translateLanguage() { String translatedLanguage = this.language.toString(); switch (this.language) { case GREEK: translatedLanguage = "Ελληνικά"; break; case ENGLISH: translatedLanguage = "English"; break; case SPANISH: translatedLanguage = "Español"; break; } return translatedLanguage; }
2.4.2 The setLanguage(RobotLanguages newLanguage) Method
In order for this function to make sense, we have the language
field private
. By doing so, we cannot access that field directly. This is where setLanguage
comes in; thanks to it, we can change the language
.
// fields; the characteristics of a robot String name; RobotColors color; private RobotLanguages language; // .... public void setLanguage(RobotLanguages newLanguage) { if(this.language != newLanguage) { this.language = newLanguage; } }
Now, you might be wondering “why are doing that instead of simply declaring language
as public
“. The reason we use a set method is that this way, we can do additional checks or operations when setting the language
field. For example, we could have the Robot
announce the change of language
with a System.out.println()
or check whether the current language
is the same as the newLanguage
and not perform any actions, since it would be unnecessary.
Normally, all fields would have setter and getter functions, but for the sake of brevity, we will not include all those methods.
2.4.3 The calculate(double operand1, double operand2, RobotOperations operation) Method
This method calculates the result of the operation
between the two double
numbers and announces the result in the appropriate language. The operation
is an enum named RobotOperations
which is defined as such:
enum RobotOperations { ADD, SUBTRACT, MULTIPLY, DIVIDE }
Just like all the enums in Robot
, we are ensuring that only acceptable arithmetic operations are provided to the Robot
.
The calculate(...)
method is the following:
public void calculate(double operand1, double operand2, RobotOperations operation) { double result = 0; switch (operation) { case ADD: result = operand1 + operand2; break; case SUBTRACT: result = operand1 - operand2; break; case MULTIPLY: result = operand1 * operand2; break; case DIVIDE: result = operand1 / operand2; break; } switch (this.language) { case GREEK: System.out.println("Το αποτέλεσμα είναι " + result); break; case ENGLISH: System.out.println("The result is " + result); break; case SPANISH: System.out.println("El resultado es " + result); break; } }
You might notice that there isn’t any check for division with zero. That’s because we won’t get an exception for that if we are dealing with float
or double
numbers, but either:
Infinity
when dividing a positive number with zero.-Infinity
when dividing a negative number with zero.- Or,
NaN
when dividing zero with zero.
3. Conclusion
By now, you should have a solid understanding of Java Classes. You can find the source code on our GitHub page.
4. Sources
[1]: Classes, Oracle