Java is an object-oriented programming language with features such as classes, objects, inheritance, polymorphism abstraction, encapsulation, exception handling, multithreading, security, etc. By using these we can build an application that is modular, manageable, and robust.
An application is a collection of multiple classes, interfaces, APIs, etc that work in conjunction to give required functionality. During the software development process, many times we might need to use a particular block of code multiple times in the application’s logic so, creating reusable code promotes code efficiency, reduces redundancy and simplifies maintenance.
Reusable code is designed to be used across multiple projects or within different parts of the same project without modification. Features like methods, and inheritance allow to write a reusable blocks of code that can be used in the application.
Guide To Write Reusable Code in Java
In Java we can write reusable codes by using the following techniques:
- Encapsulation
- Inheritance
- Interfaces
- Generics
1. Encapsulation
Encapsulation is the process of wrapping up data members and member functions into a single unit for instance class. With the help of encapsulation, the internal state of the data members can be hidden and given controlled access via methods.
These methods are usually the getter and setter methods. The setter method is used to provide/ set value to data members and the getter method is used to retrieve the set value.
Example:
Person.java
package com.example;
public class Person {
// data members
private int age;
private String name;
private String email;
// constructors
public Person() {}
public Person(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
// getters and setters
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
//toString to display object information
@Override
public String toString() {
return "person information : [" + getName() + " : " + getAge() + " : " + getEmail() + "]";
}
}
TestPerson1.java
package com.example;
public class TestPerson1 {
public static void main(String[] args) {
Person p = new Person();
p.setName("kalyani");
p.setAge(30);
p.setEmail("kalyani@itvedant.com");
System.out.println(p);
}
}
Output:
person information : [kalyani : 30 : kalyani@itvedant.com]
Explanation
In the above example, the Person class contains name, email and age data members and constructors and getters and setters. These methods can be used in any class to get-set a person’s information.
In TestPerson1.java we created a reference of the Person class and provided values to the data members using the setter methods and overridden the toString() method to display the person object information. We passed the person object ‘p’ directly to the print statement which using the toString() displays the person object information.
2. Inheritance
Inheritance refers to the process of acquiring some or all properties and behaviour of an existing class into a newly created class. The newly created class may have its own properties and behaviour too.
Inheritance involves method overriding which is a process of overriding the methods of the parent class into the child class. Here, the parent class refers to the super/ root/ base class and the child class refers to the sub/ leaf/ derived class.
In Java Inheritance can be used by extending the child class from the parent class. The keyword used is “extends”. Every class, be it a predefined class or user-defined class extends to the rootmost class Object. In Java following types of inheritance are supported:
- Single Level
- Multi-Level
- Hierarchical Level
Single Level Inheritance Example:
For this example, we will use the Person.java class built in the Encapsulation example.
Student.java
package com.example;
public class Student extends Person{
private int rollno;
private String course;
public Student(){}
public Student(String name, int age, String email, int rollno, String course) {
super(name, age, email);
this.rollno = rollno;
this.course = course;
}
public int getRollno() {
return rollno;
}
public void setRollno(int rollno) {
this.rollno = rollno;
}
public String getCourse() {
return course;
}
public void setCourse(String course) {
this.course = course;
}
public void displayCourseFees(String course) {
if(course.equals("Full-Stack")) {
System.out.println("Fees : Rs50000");
}
else if(course.equals("Data-Science")) {
System.out.println("Fees : Rs53000");
}
else {
System.out.println("Course Not Available");
}
}
@Override
public String toString() {
return super.toString() + " : " + getRollno() + " : " + getCourse();
}
}
TestStudentInheritance.java
package com.example;
public class TestStudentInheritance {
public static void main(String[] args) {
Student stud = new Student();
stud.setName("Akash");
stud.setAge(30);
stud.setEmail("akashdinde92@gmail.com");
stud.setRollno(112);
stud.setCourse("Full-Stack");
System.out.println(stud);
stud.displayCourseFees(stud.getCourse());
}
}
Output:
person information : Akash : 30 : akashdinde92@gmail.com : 112 : Full-Stack
Fees : Rs50000
Explanation
In this example, we inherited the Student class from the existing Person class. In the Student class, we added roll no and course data members. For these, we created getters and setters.
In the Student constructor, we first gave a call to the Person class constructor using super() to reuse the Person constructor logic and then assigned the values to roll no and course during the object creation.
The Student class has a displayCourseFees() method which is used to display the fees for Full-Stack and Data-Science students. The toString() method is used to return the Person class content using super.toString() and the Student class content.
3. Interfaces
An Interface is a mechanism used to achieve abstraction in Java. Now, Abstraction refers to a process of hiding the implementation details from the user and giving only the essential details/ functionality to the user.
The interface provides a contract to the implementing classes where the methods provided by the interface can be given logic by the classes. The interface contains constants and only method prototypes, in simple words interface is a collection of fixed values and abstract methods.
Example
Interface : Drawable.java
package com.example;
public interface Drawable {
void drawCircle(int radius);
}
Class implementing Drawable Interface :
package com.example;
public class DrawShapes implements Drawable {
public static void main(String[] args) {
int radius = 2;
DrawShapes draw = new DrawShapes();
draw.drawCircle(radius);
}
@Override
public void drawCircle(int radius) {
System.out.println("Drawing circle of radius " + radius);
}
}
Output:
Drawing circle of radius 2
Explanation
In the above example, the Drawable interface consists of the drawCircle(int radius) method. Now, The class DrawShapes implements the interface Drawable so it becomes the responsibility of the class to give the drawCircle() methods logic.
Interfaces provide 100% of abstraction.
Generics
Generics refers to parameterized types. The major aim behind using generics is that it allows types such as Integer, String, Byte or any User-Defined type can be passed as parameters to methods or classes or interfaces.
With the help of generics, a template can be created which can be utilized for a variety of data types. These are the same as the Templates of CPP.
Generic Methods, Classes and Interfaces can be created. Here, we will consider the example of Generic classes which work on multiple types of data.
Exampleused to retrieve the set value.
Box.java
package com.example;
public class Box<T> {
private T type;
public void put(T item) {
this.type = item;
}
public T get() {
return type;
}
}
TestGenerics.java
package com.example;
public class TestGenerics {
public static void main(String[] args) {
Box<Integer> intData = new Box<>();
intData.put(100);
System.out.println("Integer data : " + intData.get());
Box<String> stringData = new Box<>();
stringData.put("ITVedant");
System.out.println("String data : " + stringData.get());
}
}
Output:
Integer data : 100
String data : ITVedant
Explanation
In the above example, the Box class is created as a base template class which can be used to set different types. The put() method is used to set the box type to the respected data type and the get() method is used to retrieve the value that is set to the box type. Initially, the Box is put to Integer type and provided value 100 and then it is put to String type and provided value ITVedant.
This way we can use Encapsulation, Inheritance, Interfaces and Generics for code reusability.
Why is it Important to Write Reusable Code?
There are many reasons why writing reusable codes is important and how they help in effective software development:
1. Efficiency and Productivity
The usage of reusable code allows the developers to save the time and effort of writing the same code again and again in different parts of the application. Due to this productivity and efficiency increase.
2. Reduced Duplicacy
Since we will use the already written code blocks, the duplication of code is removed. The boilerplate code especially is removed by using reusable codes.
3. Consistency and Uniformity
Reusing code promotes consistency in the application development process. So changes made in the original code block will be applied to the reused code blocks as well. This helps in maintaining uniformity across the whole application being developed.
4. Code Readability and Maintainability
Writing reusable codes helps in making the codebase more modular and encapsulated. This leads to a cleaner and more maintainable codebase. Developers can easily understand and modify the existing components without getting into details.
5. Ease of Testing
The reusable block of codes can be tested individually before reusing them. This helps in finding errors at the early stages. Thus saving time required in the testing process.
6. Best Practices
Writing reusable code blocks is one of the major best practices that we can incorporate in our application development process. This makes the codebase more manageable and readable.
Conclusion
The writing of reusable code is one of the fundamental practices in the software development process which enhances efficiency, maintainability and also the quality of the software being developed.
By creating reusable code blocks, the developers can reduce efforts, time and duplicity in development and promote consistency and uniformity across the whole application. This approach leads to a more lean, clean code writing and makes the application codebase more modular. If you’re planning to become a Java developer, it’s time to check out our Java Course with 100% Job Guarantee
Want to learn how to create portable and dependable code in Java? Read: WORA in Java