Search Logic Blocks

Sunday, April 25, 2021

Java: Variables in an Interface

When I introduced interfaces in the post Java: Interfaces - I mentioned that, variables can be declared in an interface - "Variables can be declared inside the interface, but they must be initialized and they are implicitly final and static. The variables defined in the interface can not be changed (final) and can be accessed by interface (static) itself. All the members of the interface are implicitly public."

Let's write a simple program to test this concept - There is an interface IUserName, having a prototype to get the user name and display same to the console. There is a static method in the interface, which checks if the entered user name is valid, if yes, displays the user name else it displays the message accordingly. Interface has String class type variables which are used to store the messages. The class User implements the interface IUserName.

Interface IUserName (IUserName.java)
public interface IUserName {
// String MSG;
String MSGLEN = "User name should have atleast 8 characters!!";
String MSGNUM = "User name should have atleast one numeric character!!";
String MSGDOLLAR = "User name should not have dollar sign!!";

public void getUsername();

public void displayUsername();

public static boolean isValid(String name) {
boolean isValid = true;
// MSGLEN = "User should have ateast 10 characters!!";
if(name.length() < 8) {
System.out.println(MSGLEN);
isValid = false;
return isValid;
}
if(!name.matches(".*\\d.*")) {
System.out.println(MSGNUM);
isValid = false;
return isValid;
}
if(name.matches(".*[$].*")) {
System.out.println(MSGDOLLAR);
isValid = false;
return isValid;
}
return isValid;
}
}
Class User (User.java)
import java.util.Scanner;

public class User implements IUserName {
String userName = "";
public void getUsername() {
System.out.println("Enter the user name");
Scanner sc = new Scanner(System.in);
userName = sc.next();
if(IUserName.isValid(userName)) displayUsername();
}

public void displayUsername() {
System.out.println("User name is " + userName);
}

public static void main(String args[]) {
User user = new User();
user.getUsername();
// System.out.println(IUserName.MSGDOLLAR);
// System.out.println(user.MSGDOLLAR);
}
}
There are the following requirements for the user name to be valid -
  • must be more than 8 characters.
  • should have at least one numeric character.
  • dollar sign is not allowed.
String MSGLEN = "User name should have atleast 8 characters!!";
String MSGNUM = "User name should have atleast one numeric character!!";
String MSGDOLLAR = "User name should not have dollar sign!!";
The getUsername() method in the class User accepts a user name from the console. 
public void getUsername() {
System.out.println("Enter the user name");
Scanner sc = new Scanner(System.in);
userName = sc.next();
if(IUserName.isValid(userName)) displayUsername();
}
The method getUsername() calls the static method isValid() from the interface directly to check the validity of the entered user name. (The code uses a String class method matches, which takes a regular expression as a parameter and finds out whether the String matches with the regular expression. I will write a separate post to discuss regular expressions in detail.)
  • Checks whether the user name has at least 8 characters. If not, the message - User name should have atleast 8 characters!! - is printed on the console.
if(name.length() < 8) {
System.out.println(MSGLEN);
isValid = false;
return isValid;
}
  • Checks whether the user name has at least one numeric character. If not, the message - User name should have atleast one numeric character!! - is printed on the console. (".*\\d.*" - \\d means any digit and .* means any character with 0 or more occurrences.)
if(!name.matches(".*\\d.*")) {
System.out.println(MSGNUM);
isValid = false;
return isValid;
}
  • Checks whether the user name contains character $. If yes, the message - User name should not have dollar sign!! - is printed on the console. (".*[$].*" - [$] means single character and .* means any character with 0 or more occurrences.)
if(name.matches(".*[$].*")) {
System.out.println(MSGDOLLAR);
isValid = false;
return isValid;
}
If the user name is valid, the displayUsername() method displays the user name.
public void displayUsername() {
System.out.println("User name is " + userName);
}
Here is the output:

Enter the user name
Michael
User name should have atleast 8 characters!!

Enter the user name
MichaelJohn
User name should have atleast one numeric character!!

Enter the user name
Michael$4John
User name should not have dollar sign!!

Enter the user name
Michael8John
User name is Michael8John

Let's talk about the String variables used in the interface, we have the following observations -

If we try to declare the interface variable MSG without the initialization, 
String MSG;
it gives compile time error -

Error:(4, 15) java: = expected

Conclusion - The interface variables must be initialized.

If we  try to assign the new value to the interface variable MSGLEN,
MSGLEN = "User should have ateast 10 characters!!";
it gives compile time error -

Error:(15, 9) java: cannot assign a value to final variable MSGLEN

Conclusion - The interface variables are implicitly final. They can't be changed.

If we try to access the interface variables directly using the interface name, or through the instance of the class implementing the interface,
System.out.println(IUserName.MSGDOLLAR);
System.out.println(user.MSGDOLLAR);
there will be no compile time error -

Conclusion - The interface variables are implicitly static. They can be accessed through the interface directly as well as by the instance of the class implementing the interface.

NOTE: So, above code verifies that the variables declared in th interface are implicitly final and static.

The code can be accessed at Github Link.

Thursday, April 22, 2021

Java: Static Methods in an Interface

In our last post - Default Methods in an Interface, we saw that how we can add a default method to an existing interface and how we can override the default method in the classes, which are implementing the interface. Like a regular class, we can also have a static method in an interface and can be accessed the same way. There is no need to implement or extend the interface to access the static method.

Let's create an interface which represents an array of integer numbers. Array can be series of any kind of numbers - Prime numbers, Fibonacci numbers, Even numbers, Odd numbers or any other pattern. The interface provides a default method to create a list of integers, methods to implement the interface and a static method to display the group data in a standard way.


Interface Group - (Group.java)
public interface Group {
public int getNextNo();

public int[] getList();

public String getName();

default public int[] createGroup(int n) {
int num[] = new int[n];
for(int i = 0; i < n; i++) {
num[i] = getNextNo();
}
return num;
}

static public void displayGroup(Group g) {
int num[] = g.getList();
System.out.println(g.getName());
int n = num.length;
for(int i = 0; i < n; i++) {
System.out.print(num[i] + " ");
}
System.out.println(" ");
}
}
Methods defined in the interface -
public int getNextNo();
getNextNo() method is used to generate the new number for the particular group. This method will be implemented different for every group of numbers - prime numbers, Fibonacci numbers, etc.
public int[] getList();
getList() method returns the array of integers numbers generated in the particular group - prime, fibonacci, etc.
public String getName();
getName() method returns the name of the particular list.
default public int[] createGroup(int n) {
int num[] = new int[n];
for(int i = 0; i < n; i++) {
num[i] = getNextNo();
}
return num;
}
createGroup() method is the default method to create the list of numbers. The implementing class does not need to implement the createGroup() method, but can override the method as needed.
static public void displayGroup(Group g) {
int num[] = g.getList();
System.out.println(g.getName());
int n = num.length;
for(int i = 0; i < n; i++) {
System.out.print(num[i] + " ");
}
System.out.println(" ");
}
displayGroup() method displays the list in a particular way. This method is static method, so can be accessed directly. This method can not be overridden. In the implementing class, if there is any method with the same name, it will be totally different method and there will be no relation to static method in the interface.

Class Prime (Prime.java) for prime numbers - getNextNo() method returns the next prime number in the list
public class Prime implements Group {
int lastPrime = 1;
int groupSize = 0;
int primeList[];

Prime(int n) {
groupSize = n;
primeList = createGroup(n);
}

public int getNextNo() {
if(lastPrime >= 3) {
boolean foundNext = false;
int temp = lastPrime;
while(!foundNext) {
temp = temp + 2;
if(isPrime(temp)) {
lastPrime = temp;
foundNext = true;
}
}
} else if (lastPrime == 1) lastPrime = 2;
else if (lastPrime == 2) lastPrime = 3;
return lastPrime;
}

public int[] getList() {
return primeList;
}

public String getName() {
return "Prime Numbers List: ";
}

public boolean isPrime(int n) {
boolean prime = true;
for(int j = 3; j < n; j = j + 2) {
if((n % j) == 0) {
prime = false;
break;
}
}
return prime;
}
}
Class Fibo (Fibo.java) for Fibonacci numbers - getNextNo() returns the next Fibonacci number in the list
public class Fibo implements Group {
int lastFibo1 = 0;
int lastFibo2 = 0;
int groupSize = 0;
int FiboList[];

Fibo(int n) {
groupSize = n;
FiboList = createGroup(n);
}

public int getNextNo() {
if(lastFibo1 >= 1 && lastFibo2 >= 1) {
int temp = lastFibo2;
lastFibo2 = lastFibo2 + lastFibo1;
lastFibo1 = temp;
} else if (lastFibo2 == 0) lastFibo2 = 1;
else if (lastFibo2 == 1) {
lastFibo1 = 1;
}
return lastFibo2;
}

public int[] getList() {
return FiboList;
}

public String getName() {
return "Fibonacci Numbers List: ";
}
}
Class GroupDemo (GroupDemo.java) - Demos how to access the interface Group, to access its default and static methods, and its implementing classes (Prime, Fibo, Even and Odd) -
(NOTE: Above, I have not included code for classes Even and Odd. But these classes can be accessed at Github.)
public class GroupDemo {
public static void main(String args[]) {
Group g1 = new Prime(10);
Group.displayGroup(g1);
Group g2 = new Fibo(10);
Group g3 = new Odd(10);
Group.displayGroup(g2);
Group.displayGroup(g3);
Group g4 = new Even(10);
Group.displayGroup(g4);
}
}
Output

Prime Numbers List: 
2 3 5 7 11 13 17 19 23 29  
Fibonacci Numbers List: 
1 1 2 3 5 8 13 21 34 55  
Odd Numbers List: 
3 5 7 9 11 13 15 17 19 21  
Even Numbers List: 
2 4 6 8 10 12 14 16 18 20  

All the code can be accessed at Github Link.

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.