Search Logic Blocks

Sunday, April 18, 2021

Java: Default Methods in an Interface

As we know that an interface is like a prototype, which has the methods with empty bodies and the interfaces can't be instantiated (means instance or object can't be created) like the classes. Interfaces can only be implemented by the classes or extended into another interface (we will know about inherited interfaces later). That means, once the interface is used by other classes or interfaces, it is not possible to make changes in the existing interface.

To give the flexibility to change an existing interface, without affecting it's implementing classes or inheriting interfaces, a new feature was added at the time of release of JDK8 - The default methods.

Default Methods in an Interface -
Default methods are the methods with default code statements (not the empty body), which can be added to an existing interface without any effect on the classes and interfaces using the existing interface. The new added default method has its own body, so the classes don't need to provide their implementation and they will work same as earlier before adding the default method.

Let's take the same example used in the post Introduction to Interfaces - IPolygon is an Interface, Triangle and Rectangle are the classes implementing the same interface IPolygon, and PolygonTest is a demo class showing how to use the methods of both the classes.


If we add an empty body method to the existing interface IPolygon, we need to change both the classes to implement the new method. But, if we add a new default method to the interface - there will be no effect to the classes - Triangle and Rectangle. Objects of both the classes can call the default method without  any  implementation of the method in the classes.


Default methods are created by using the keyword default. And method definition is same as like a regular method. Let's have a default method printShapeDetails(), which prints all the details of the specific shape. The code for the new default method will look like this -
default public void printShapeDetails() {
System.out.println("Shape is " + whatShape());
System.out.println("Total Sides are " + noOfSides());
System.out.println("Sides are: ");
double sides[] = getSides();
for(int i = 0; i < getSides().length; i++) {
System.out.print("Side " + (i+1) + ": " + sides[i] + "; ");
}
System.out.println();
System.out.println("Area is " + area());
System.out.println("Perimeter is " + perimeter());
System.out.println();
}
And without changing anything in both the classes - Triangle and Rectangle, the instances of these classes can call the default method, like we call other methods in the demo class PolygonTest.

Interface IPolygon
// Interface Polygon gives structure of the methods needed to work with any polygon
// Every class implementing this interface should have all the methods
public interface IPolygon {
// Returns the text telling what shape it is
public String whatShape();

// Returns the numbers of sides
public int noOfSides();

// Returns the array of lengths of all sides
public double[] getSides();

// Returns the area of the shape
public double area();

// Returns the perimeter of the shape
public double perimeter();

default public void printShapeDetails() {
System.out.println("Shape is " + whatShape());
System.out.println("Total Sides are " + noOfSides());
System.out.println("Sides are: ");
double sides[] = getSides();
for(int i = 0; i < getSides().length; i++) {
System.out.print("Side " + (i+1) + ": " + sides[i] + "; ");
}
System.out.println();
System.out.println("Area is " + area());
System.out.println("Perimeter is " + perimeter());
System.out.println();
}
}
Class Triangle
// Class Triangle implementing the interface IPolygon
public class Triangle implements IPolygon {
final int noOfSides = 3;
double side1;
double side2;
double side3;
double perimeter;
double area;

// Triangle Constructor with no arguments
Triangle() {
this.side1 = 4;
this.side2 = 4;
this.side3 = 4;
}

// Triangle Constructor with three arguments
Triangle(double side1, double side2, double side3) {
this.side1 = side1;
this.side2 = side2;
this.side3 = side3;
}

// Returns the text telling what shape it is
public String whatShape() {
return "Triangle";
}

// Returns the numbers of sides
public int noOfSides() {
return noOfSides;
}

// Returns the array of lengths of all sides
public double[] getSides() {
double arrSides[] = new double[3];
arrSides[0] = side1;
arrSides[1] = side2;
arrSides[2] = side3;
return arrSides;
}

// Returns the area of the triangle
public double area() {
double p, diff1, diff2, diff3, pr;
p = perimeter / 2;
diff1 = p - side1;
diff2 = p - side2;
diff3 = p - side3;
pr = p * diff1 * diff2 * diff3;
area = Math.round(Math.sqrt(pr) * 100) / 100.0;
return area;
}

// Returns the perimeter of the triangle
public double perimeter() {
perimeter = Math.round((side1 + side2 + side3) * 100) / 100.0;
return perimeter;
}
}
Class Rectangle
// Class Rectangle implementing the interface IPolygon
public class Rectangle implements IPolygon {
final int noOfSides = 4;
double side1;
double side2;
double perimeter;
double area;

// Rectangle Constructor with no arguments
Rectangle() {
this.side1 = 4;
this.side2 = 4;
}

// Rectangle Constructor with three arguments
Rectangle(double side1, double side2) {
this.side1 = side1;
this.side2 = side2;
}

// Returns the text telling what shape it is
public String whatShape() {
return "Rectangle";
}

// Returns the numbers of sides
public int noOfSides() {
return noOfSides;
}

// Returns the array of lengths of all sides
public double[] getSides() {
double arrSides[] = new double[4];
arrSides[0] = side1;
arrSides[1] = side2;
arrSides[2] = side1;
arrSides[3] = side2;
return arrSides;
}

// Returns the area of the rectangle
public double area() {
double pr;
pr = side1 * side2;
area = Math.round(pr * 100) / 100.0;
return area;
}

// Returns the perimeter of the rectangle
public double perimeter() {
perimeter = Math.round((2 * (side1 + side2)) * 100) / 100.0;
return perimeter;
}
}
Class PolygonTest
// Class for testing the interface IPolygon and its implementing classes
public class PolygonTest {
public static void main(String args[]) {
IPolygon P[] = new IPolygon[2];
P[0] = new Triangle(16.6, 11.2, 17.9);
P[1] = new Rectangle(12.5, 15.5);

for(int i = 0; i < P.length; i++) {
System.out.println("Polygon " + (i+1) + " is " + P[i].whatShape());
System.out.println("No of sides: " + P[i].noOfSides());
double allSides[] = P[i].getSides();
System.out.print("All sides are : ");
for(int j = 0; j < allSides.length; j++) {
System.out.print("Side" + (j+1) + ": " + allSides[j] + " ");
}
System.out.println();
System.out.println("Perimeter is : " + P[i].perimeter());
System.out.println("Area is : " + P[i].area());
System.out.println();
}

for(int i = 0; i < P.length; i++) {
P[i].printShapeDetails();
}
}
}
And the output is here :-

Polygon 1 is Triangle
No of sides: 3
All sides are : Side1: 16.6   Side2: 11.2   Side3: 17.9   
Perimeter is : 45.7
Area is : 90.75

Polygon 2 is Rectangle
No of sides: 4
All sides are : Side1: 12.5   Side2: 15.5   Side3: 12.5   Side4: 15.5   
Perimeter is : 56.0
Area is : 193.75

Shape is Triangle
Total Sides are 3
Sides are: 
Side 1: 16.6; Side 2: 11.2; Side 3: 17.9; 
Area is 90.75
Perimeter is 45.7

Shape is Rectangle
Total Sides are 4
Sides are: 
Side 1: 12.5; Side 2: 15.5; Side 3: 12.5; Side 4: 15.5; 
Area is 193.75
Perimeter is 56.0

Now, we can create a new class by implementing the interface updated with new default method and override the default method in the new class. Let's add a new class called Square and write a separate method printShapeDetails() in the class Square to show the details of the square -

Class Square
// Class Square implementing the interface IPolygon
public class Square implements IPolygon {
final int noOfSides = 4;
double side;
double perimeter;
double area;

// Square Constructor with no arguments
Square() {
this.side = 4;
}

// Square Constructor with one argument
Square(double side) {
this.side = side;
}

// Returns the text telling what shape it is
public String whatShape() {
return "Square";
}

// Returns the numbers of sides
public int noOfSides() {
return noOfSides;
}

// Returns the array of lengths of all sides
public double[] getSides() {
double arrSides[] = new double[4];
arrSides[0] = side;
arrSides[1] = side;
arrSides[2] = side;
arrSides[3] = side;
return arrSides;
}

// Returns the area of the square
public double area() {
double pr;
pr = side * side;
area = Math.round(pr * 100) / 100.0;
return area;
}

// Returns the perimeter of the square
public double perimeter() {
perimeter = Math.round((4 * side) * 100) / 100.0;
return perimeter;
}

public void printShapeDetails() {
System.out.println("Shape is " + whatShape());
System.out.println("Total Sides are " + noOfSides());
System.out.println("Sides of the square are: " + this.side);
System.out.println("Area is " + area());
System.out.println("Perimeter is " + perimeter());
System.out.println();
}
}
Changes in Class PolygonTest
// Class for testing the interface IPolygon and its implementing classes
public class PolygonTest {
public static void main(String args[]) {
IPolygon P[] = new IPolygon[3];
P[0] = new Triangle(16.6, 11.2, 17.9);
P[1] = new Rectangle(12.5, 15.5);
P[2] = new Square(4);

for(int i = 0; i < P.length; i++) {
System.out.println("Polygon " + (i+1) + " is " + P[i].whatShape());
System.out.println("No of sides: " + P[i].noOfSides());
double allSides[] = P[i].getSides();
System.out.print("All sides are : ");
for(int j = 0; j < allSides.length; j++) {
System.out.print("Side" + (j+1) + ": " + allSides[j] + " ");
}
System.out.println();
System.out.println("Perimeter is : " + P[i].perimeter());
System.out.println("Area is : " + P[i].area());
System.out.println();
}

for(int i = 0; i < P.length; i++) {
P[i].printShapeDetails();
}
}
}
And the output is here -

Polygon 1 is Triangle
No of sides: 3
All sides are : Side1: 16.6   Side2: 11.2   Side3: 17.9   
Perimeter is : 45.7
Area is : 90.75

Polygon 2 is Rectangle
No of sides: 4
All sides are : Side1: 12.5   Side2: 15.5   Side3: 12.5   Side4: 15.5   
Perimeter is : 56.0
Area is : 193.75

Polygon 3 is Square
No of sides: 4
All sides are : Side1: 4.0   Side2: 4.0   Side3: 4.0   Side4: 4.0   
Perimeter is : 16.0
Area is : 16.0

Shape is Triangle
Total Sides are 3
Sides are: 
Side 1: 16.6; Side 2: 11.2; Side 3: 17.9; 
Area is 90.75
Perimeter is 45.7

Shape is Rectangle
Total Sides are 4
Sides are: 
Side 1: 12.5; Side 2: 15.5; Side 3: 12.5; Side 4: 15.5; 
Area is 193.75
Perimeter is 56.0

Shape is Square
Total Sides are 4
Sides of the square are: 4.0
Area is 16.0
Perimeter is 16.0

All the code can be found at Github Link.

Monday, March 29, 2021

Java: Nested Interface in an Interface

We had talked about interfaces and their fully / partial implementations. We also talked about Nested Interface in a Class (declaring an interface in a class) and Nested Class in an Interface (declaring a class in am interface). Let's declare an interface inside an interface (Nested Interface in an Interface) -

The inner interface class instance can be used three ways -
  • instance methods can be called directly
  • instance is passed to interface variable by reference and then methods are called through that variable
  • instance is passed to the outer interface method which takes a parameter interface variable

All three examples are shown in the code given below -
// Outer Interface
public interface OuterInterface {
public void display();
public void showInner(InnerInterface i);

// Inner Interface
public interface InnerInterface {
public void display();
}
}
// Implementation of Outer Interface
public class OuterInterfaceClass implements OuterInterface {
public void display() {
System.out.println("Display method from OuterInterfaceClass");
}

public void showInner(InnerInterface i) {
System.out.println("Show method from OuterInterfaceClass");
i.display();
}
}
// Implementation of Inner Interface
public class InnerInterfaceClass implements OuterInterface.InnerInterface {
public void display() {
System.out.println("Display method from InnerInterfaceClass");
}
}
// Demo class showing accessing inner interface in all three ways
public class Test {
public static void main (String args[]) {
// Creates a an object of class implementing OuterInterface
OuterInterfaceClass oic = new OuterInterfaceClass();
oic.display();
System.
out.println("___________________________________________________");

// Creates a an object of class implementing InnerInterface
InnerInterfaceClass iic = new InnerInterfaceClass();
iic.display();
System.
out.println("___________________________________________________");

// Creates a a variable of inner interface and assign the class variable By Reference
OuterInterface.InnerInterface oiii;
oiii = iic;
oiii.display();
System.
out.println("___________________________________________________");
// Calls the outer interface method which accesses inner interface
oic.showInner(iic);
}
}
Let's explore all three options -

InnerInterfaceClass is created by implementing inner interface InnerInterface directly.
// Implementation of Inner Interface
public class InnerInterfaceClass implements OuterInterface.InnerInterface {
public void display() {
System.out.println("Display method from InnerInterfaceClass");
}
}
1. instance methods can be called directly -

Create an instance of class implementing inner interface and then call interface methods:
// Creates a an object of class implementing InnerInterface
InnerInterfaceClass iic = new InnerInterfaceClass();
iic.display();
2. instance is passed to an interface variable by reference and then methods are called through that variable -

Declare a variable of inner interface and assign the inner class variable (Using class reference), call the method through interface variable:
// Creates a a variable of inner interface and assign the class variable By Reference
OuterInterface.InnerInterface oiii;
oiii = iic;
oiii.display();
3. instance is passed to the outer interface method which takes a parameter interface variable -

Creates an instance of class implementing outer interface and then call interface methods passing inner interface as a parameter:
// Creates a an object of class implementing OuterInterface
OuterInterfaceClass oic = new OuterInterfaceClass();
// Calls the outer interface method which accesses inner interface
oic.showInner(iic);
Here is the output of the above code -

Display method from OuterInterfaceClass
___________________________________________________
Display method from InnerInterfaceClass
___________________________________________________
Display method from InnerInterfaceClass
___________________________________________________
Show method from OuterInterfaceClass
Display method from InnerInterfaceClass

Code can be accessed on Github Link.