OOP is a programming concept of using objects, which contains attributes (data) and/or methods (actions).
With this we can be able to describe different things. An example of this would be using OOP to describe a car. It would have attributes (color, size etc) and actions (drive, brake etc). Another example would be describing a person, with both attributes (height, weight) and actions (sleep, study, work etc)
OOP has four main concepts:
Inheritance is where we build a new class from an existing class. Here the new class can have attributes and methods from another class, while adding on new features.
The new class is commonly termed as a child class (alternatively subclass, extended-class), while the existing class is termed as a parent class (alternatively super-class, base-class).
Let's take the example of buildings such as an appartment, a mansion, a mud-house and an office. Despite all being totally different, they will all have common features such as doors, windows, floors etc. Due to this, we can have a parent class with the common features, then each class could have its unique features.
This can be represented by having the below classes:
Building class, which is the parent class and contains the attributes and methods that are common among all the other classes.
public class Building {
int doors;
int windows;
int floors;
Building(int doors, int windows, int floors){
this.doors = doors;
this.windows = windows;
this.floors = floors;
}
public void window_material(){
System.out.println("The windows are made of glass");
}
public void structure(){
System.out.println("This building has " + this.doors + " doors, "
+ this.windows + " windows, and "
+ this.floors + " floors.");
}
}
Appartment class, which is a child class and extends the Building parent class
public class Apartment extends Building{
int tenants;
Apartment(int doors, int windows, int floors, int tenants) {
super(doors, windows, floors);
this.tenants = tenants;
}
public void tenants(){
System.out.println("There are " + this.tenants + " tenants in this apartment.");
}
}
Office class, which is a child class and extends the Building parent class
public class Office extends Building{
String company_name;
Office(int doors, int windows, int floors, String company_name) {
super(doors, windows, floors);
this.company_name = company_name;
}
public void companies(){
System.out.println("There are 20 companies here in " + this.company_name);
}
}
We can now see the implementation of the objects from the classes in the main class as below:
public class Main {
public static void main(String[] args) {
Office office = new Office(12, 2, 3, "The office" );
// inherited methods
office.window_material();
office.structure();
// un-inherited method from the Office class
office.companies();
System.out.println("\n");
Apartment dopeFlats = new Apartment(15, 20, 5, 50);
// inherited methods
dopeFlats.window_material();
dopeFlats.structure();
// un-inherited method from the Apartment class
dopeFlats.tenants();
}
}
/*
Result output:
The windows are made of glass
This building has 12 doors, 2 windows, and 3 floors.
There are 20 companies here in The office
The windows are made of glass
This building has 15 doors, 20 windows, and 5 floors.
There are 50 in this apartment.
*/
the super keyword is used to access attributes and methods from the parent class
This is where we only show what is needed about the object to the user. This therefore means we do not need to know everything about the inner workings of the object. The main reason for abstraction therefore is to provide security, by hiding the workings of some features and only providing what needs to be seen.
We can use the example of a car, where we can drive from one point to another without having to know the inner workings of the engine.
In Java, we implement abstraction through interfaces or abstract classes
An abstract class is declared with abstract, where the class can not be instantiated and has to be extended (inheritance).
We can have abstract classes and/or abstract methods.
Abstract methods are important in enforcing a certain structure that you would want from inherited classes. ie you're saying that all chils classes need to have this method(s).
Let's take the example of three classes:
We first start implementing the Animal abstract class as below. We have a constructor, two abstract methods (no method body) and one normal method (a normal method has to have a body)
package Animals;
abstract class Animal {
Animal(){
System.out.println("Animal class created");
}
public abstract void breath();
public abstract String eats();
public void walk(){
System.out.println("Animal is walking");
}
}
Next we have the Human class which extends the Animal class. We have to implement the abstract mothods from the Animal abstract class, which is implemented as below. In addition we can have our own additional methods that are not in the parent class.
package Animals;
public class Human extends Animal{
String name;
int height;
// class constructor
Human(String name, int height){
this.name = name;
this.height = height;
}
public void breath(){
System.out.println(name + " is breathing");
}
@Override
public String eats() {
return name + " eats meat and greens.";
}
public int height(){
return height;
}
}
Finally we have the Cat class, also extending the Animal class. We can see that as long as it extends the abstract class, we have to include the abstract methods defined in the parent class.
package Animals;
public class Cat extends Animal{
public void breath(){
System.out.println("Cat is breathing");
}
@Override
public String eats() {
return "Cat eats meat";
}
}
The above classes will now be instantiated and executed as below from the Main class.
package Animals;
public class Main {
public static void main(String[] args){
Human cena = new Human("John Cena",100);
System.out.println("The height of " + cena.name + " is " + cena.height);
cena.breath();
cena.walk();
System.out.println(cena.eats());
System.out.println("\n");
Cat gato = new Cat();
gato.breath();
gato.walk();
System.out.println(gato.eats());
}
}
/*
The result of the above is:
Animal class created
The height of John Cena is 100
John Cena is breathing
Animal is walking
John Cena eats meat and greens.
Animal class created
Cat is breathing
Animal is walking
Cat eats meat
*/
From the above we can see that:
We can actually also use abstract classes to help implement interfaces
Example, we have three classes:
The interface has two methods. Note that any method in an interface is assumed to be an abstract method.
public interface Test_interface {
public void test_1();
public void test_2();
}
The abstract class then implements the interface. In addition it contains and implements the first method from the interface as a normal method.
abstract class Test_abstract implements Test_interface{
@Override
public void test_1() {
System.out.println("implementing test 1 method from the interface class through the abstract class");
}
}
We can then have the normal class (Test_class) extend the abstract class (Test_abstract), which is implementing the interface (Test_interface)
public class Test_class extends Test_abstract {
public void test_2(){
System.out.println("Implementing test 2 method from interface class through the test_class");
}
}
The above three classes can then be run in the main class as below:
public class Main {
public static void main(String[] args) {
Test_interface int_object = new Test_class();
int_object.test_1();
int_object.test_2();
}
}
The above can be illustrated as below:
Interfaces work in a similar way to abstract classes, but with some differences such as:
implements
instead of the extends
keyword.If an interface has only one method, it is termed as a functional interface.
Alternatively it is also known as SAM (Single Abstract Method)
In addition one can annotate it with the @FunctionalInterface
annotation. This is optional.
An interface with no method can be termed as a marker interface
// an interface with one method
interface TestInterface {
public void interfaceMethod();
}
// a class that implements the interface.
class TestClass implements TestInterface {
public void interfaceMethod(){
System.out.println("This is the implementation of a method from an interface");
}
}
public class Main {
public static void main(String[] args) {
TestClass obj = new TestClass();
obj.interfaceMethod();
}
}
Let's consider the interface TestInterface
defined below with only one method.
interface TestInterface {
public void interfaceMethod();
}
Naturally we would need a normal class that implements the interface, because we can not instantiate the interface.
Programmatically, this would be:
// interface
@FunctionalInterface
interface TestInterface {
public void interfaceMethod();
}
// normal class
class TestClass implements TestInterface {
public void interfaceMethod(){
System.out.println("This is the implementation of a method from an interface");
}
}
// object creation and implementation of interface method
public class Test {
public static void main(String[] args) {
TestClass obj = new TestClass();
obj.interfaceMethod();
}
}
One can bypass the use of another class to implement the interface in two ways:
An Anonymous inner class is a class within a class, and it has no name. With this concept, we can then have the above code implementation re-written as:
@FunctionalInterface
interface TestInterface {
public void interfaceMethod();
}
//class TestClass implements TestInterface {
// public void interfaceMethod(){
// System.out.println("This is the implementation of a method from an interface");
// }
//}
public class Test {
public static void main(String[] args) {
TestInterface obj = new TestInterface(){
public void interfaceMethod(){
System.out.println("This is the implementation of a method from an interface");
}
};
obj.interfaceMethod();
}
}
We have successfully skipped the process of creating the class that implements the interface, and put the class content as we instantiate the object through the interface. This also goes to show that we can also instantiate an object with an interface through inner classes.
We can go a step further and use lambda expressions, which are implementations of functional interfaces (interface with one abstract method). This therefore also allows the implementation of interfaces without the use of another class.
Lambda expressions follow the format of
// a simple lambda expression with one statement
() -> statement;
// multiple statements in a code block
() -> {
statement;
statement;
};
// passing parameters
(param1, param2) -> {
statement;
statement;
};
The above code examples can therefore be rewritten with a lambda expression as:
@FunctionalInterface
interface TestInterface {
public void interfaceMethod();
}
//class TestClass implements TestInterface {
// public void interfaceMethod(){
// System.out.println("This is the implementation of a method from an interface");
// }
//}
public class Test {
public static void main(String[] args) {
TestInterface obj = () -> System.out.println("This is the implementation of a method from an interface");
obj.interfaceMethod();
}
}
We pass parameters to the lambda expression and have multiple statements as below:
@FunctionalInterface
interface TestInterface {
public void interfaceMethod(String name, int age);
}
public class Test {
public static void main(String[] args) {
TestInterface obj = (name, age) -> {
System.out.println("The name of the superhero is " + name);
System.out.println(name + " is " + age + " years");
};
obj.interfaceMethod("Batman", 40);
}
}
It is clear that use of Lambda expressions are cleaner and readable as compared to Inner classes
Polymorphism is the concept of having methods take have behaviours.
Polymorphism is categirised into two main areas:
This takes place when the program is being compiled, and in this case, it is the compiler that decides which method is going to be called. This is done through method overloading, where a method is
This happens when the program is being executed, and it is the JVM (Java Virtual Machine) that decides which method to be used. This happens during run-time, when the user is interacting with the program
Encapsulation is where we restrict access to the attributes and/or methods of a class. The main purpose of encapsulation is to secure attributes and methods from external interference.
© Copyright Kifaru Codes. All Rights Reserved.
Designed by KifaruCodes