Friday, June 18, 2021

Java Tutorial: Packages

Chapters

Java Package

Package in java is a way to group classes, interfaces and subpackages in order to make our workspace to be organize,compact and tidy. Packages can be used for:

1.) Resolving name conflicts. For example, computer_science.Students and information_technology.Students.

2.) Providing accessibility controls. protected and default access modifiers affect the visibility of java elements through package. For example, a method with protected access modifier can only be seen in the package where its class resides or in a sub class of its class.

There's lots of built-in packages that java provides like javax.awt.swing, java.io, java.util, etc.

Creating Our Own Package

Creating a package is easy, we just need to create a folder and add a package statement with the name of the folder in our source code.
//create a directory and name it "p1"
//then add a package statement with
//the package keyword and package name
//and don't forget to put the source
//file in that package
package p1;

public class SampleClass{

  public static void main(String[]args){
    System.out.println("This class is in p1 package!");
  }
}
Note: package statement must be the first statement in a source file if we want that source file to be part of a package. Also, there's only one package statement in a source file.

Creating Subpackage

A subpackage is a package in a package. To create a subpackage, create a package in a package and then write the package statement with the name of the parent package/s and the subpackage separated by (.)dot.
//This is how you write
//a package statement for
//subpackage
package p1.sub1;

public class MySubpackage{

  public static void main(String[]args){
    System.out.println("This package is in p1.sub1 package!");
  }
}
Importing Packages

import statement allows classes to import other classes from other packages. If a source file is not in a package then, the import statement must come first before the class or interface. Otherwise, package statement must come first then the import statement comes next and then the class or interface.
//package statement
package p1;

//import statements
//create import statement by
//writing the import keyword
//first then the package name
//then the class/interface that
//we wanna import. The package
//name and class/interface is 
//separated by (.)dot.

//importing user-defined package
//p1 is a parent package, sub1
//is a subpackage and MySubpackage
//is a class/interface
import p1.sub1.MySubpackage;

//importing built-in packages
//java is a parent package, util
//is a subpackage and Random is
//a class/interface
import java.util.Random;

//importing packages using (*)wildcard
//(*) means all classes in a package
//will be imported.
import java.io.*;

public class SampleClass{

  public static void main(String[]args) throws IOException{
    
    //This class can now use the Random class
    //in java.util package.
    Random random = new Random();
    int rand = random.nextInt();
    System.out.println("Random number: " + rand);
    
    //declaring unimported class/interface by
    //explicitly including its package in the 
    //declaration.
    java.util.ArrayList<Integer> aList =
    new java.util.ArrayList<Integer>();
    
    //This class can now use every accessible
    //class/interface in java.io package.
    File file = new File("C:"+File.separator+"tmp"+
                         File.separator+"text.txt");
    if(!file.exists()){
      //This file writing process may likely 
      //work if you have a tmp folder in C: drive
      //and text.txt doesn't exist yet
      FileWriter writer = new FileWriter(file);
      writer.write(String.valueOf(rand));
      writer.close();
    }
  }
}
Static Import

Static import helps us access static class members without the need to include the class name where they belong. Static import is sometimes used when a parent class has long name.
//This source file must be in p1 folder
package p1;

public class FileOperations{
 private static String name = "FileOperations";
 
 public enum FileType{
   ZIP,TXT
 }
 
 public static void displayName(){
   System.out.println(name);
 }
 
}

//This source file must be in p2 folder
package p2;

import p1.FileOperations;

public class SampleClass{

  public static void main(String[]args){
    FileOperations.FileType type1 = FileOperations.FileType.ZIP;
    FileOperations.FileType type2 = FileOperations.FileType.TXT;
    
    if(type1 == FileOperations.FileType.ZIP &&
       type2 == FileOperations.FileType.ZIP)
       System.out.println("type1 and type2 are both zip files!");
    else
      System.out.println("type1 and type2 are not both zip files!");
     
    FileOperations.displayName();
  }
  
}
In the example above, we use the normal import statement. Let's try using static import.
//This source file must be in p1 folder
package p1;

public class FileOperations{
 private static String name = "FileOperations";
 
 public enum FileType{
   ZIP,TXT
 }
 
 public static void displayName(){
   System.out.println(name);
 }
 
}

//This source file must be in p2 folder
package p2;

//We're accessing static class members that's
//why we use (.)dot next to the FileOperations Class
//import static p1.FileOperations.FileType;

//if we want to access all accessible static members
//in a class use (*)wildcard
import static p1.FileOperations.*;

public class SampleClass{

  public static void main(String[]args){
    //Note: enum class constants in java are public, 
    //static and final by default
    FileType type1 = FileType.ZIP;
    FileType type2 = FileType.TXT;
    
    if(type1 == FileType.ZIP &&
       type2 == FileType.ZIP)
       System.out.println("type1 and type2 are both zip files!");
    else
      System.out.println("type1 and type2 are not both zip "
                         +"files!");
    //displayName() from FileOperations class       
    displayName();
  }
}
As you can see, we don't need to write the lenghty class name before the enum class name, which is a convenience.

Note: Use static import sparingly. In big applications, static import may confuse people who will read your code.

Ambiguous Static Import

Java will throw a compile-time error if two static members with identical names are accessed at the same time in the same class/interface.
import static java.lang.Long.*;
import static java.lang.Integer.*;

public class SampleClass{

  public static void main(String[]args){
    System.out.println(valueOf("FF",16));
  }
}
valueOf(String s,int radix) exists in both Long and Integer classes. Java didn't know which valueOf(String s,int radix) to call. That's why java gave up and threw a compile-time error.

Relationship Between Package And its subpackages

In terms of package structure, packages and subpackages are related to each other. That's why when we put a source file in a subpackage, we need to include the parent package and then the subpackage name in that source file package statement.

In terms of package direct access to its subpackages, packages and subpackages are not related to each other. That's why when we import a package, only the package content are imported except for subpackage content.

For example, If we import AWT classes like this: java.awt.* the event subpackage of awt package won't be included.
import java.awt.*;
//uncomment this to fix the error
//import java.awt.event.*;

public class SampleClass{

  public static void main(String[]args){
  }
}

//Error: MouseAdapter can't be found because
//MouseAdapter is in event subpackage
class MyMouseEvent extends MouseAdapter{
}
Note: It's preferrable to use the *(wildcard) if you would likely use all the classes in a package. Otherwise, specify the classes that you're going to use.

That's why classes that are in a parent package don't have direct access to the subpackage content of the parent package. Those classes still need to import the subpackage first before they can access the subpackage content.

Thursday, June 17, 2021

Java OOPs: Relationship Concepts(IS-A and HAS-A)

Chapters

Java "IS-A" Relationship

"IS-A" relationship is also known as Inheritance. It can be divided into two types: class inheritance also known as generalization and interface inheritance also known as realization.

Java "HAS-A" Relationship

"HAS-A" relationship refers to objects that are in possession of classes. It is also known as association.

Association

Association integrates relationship of classes by using an object reference. Association relationships can be one to one, one to many, many to one, and many to many.

Aggregation

Aggregation is a type of association where objects that are in possession of a class are independent from each other.
public class SampleClass{
  
  //The association here is many to one. many
  //object references in one class
  public static void main(String[]args){
    Hammer hammer = new Hammer("hammer");
    Drill drill = new Drill("drill");
    //create toolbox with items
    Toolbox toolBox = new Toolbox(hammer,drill);
    toolBox.checkItems();
    
    //create toolbox without items
    toolBox = new Toolbox();
    toolBox.checkItems();
    
    hammer.smash();
    drill.drill();
  }
}

abstract class Tools{
  private String name;
  
  Tools(String name){
    this.name = name;
  }
  
  public String getName(){return name;}
}

class Hammer extends Tools{

  Hammer(String name){
    super(name);
  }
  
  public void smash(){
    System.out.println("Hammer Smash!");
  }
}

class Drill extends Tools{

  Drill(String name){
    super(name);
  }
  
  public void drill(){
    System.out.println("Drill!");
  }
}

class Toolbox{
  private Tools[] tools;
  
  Toolbox(Tools... tools){
    this.tools = new Tools[tools.length];
    
    for(int i = 0; i < tools.length; i++)
      this.tools[i] = tools[i];
  }
  
  public void checkItems(){
    if(tools.length != 0){
      System.out.println("Items in the Toolbox");
      for(Tools tool : tools)
        System.out.println(tool.getName());
    }
    else
      System.out.println("There are no tools in the Toolbox!");
    
  }
  
}
In the example above, SampleClass class possesses Toolbox and Tools object references. the Toolbox object can still function as a toolbox even without tools. On the other hand, Tools objects can still function as tools without toolbox. We can check items in the Toolbox object even there are no Tools object in it and we can use tools even without toolbox. That's why Toolbox object is independent from Tools object and vice-versa.

Composition

Composition is a type of association where objects that are in possession of a class are dependent from each other.
public class SampleClass{

  public static void main(String[]args){
    
    /*
    RandomAccessMemory ram = null;
    Motherboard board = new Motherboard();
    
    //error: can't use Motherboard 'cause there's
    //no RAM
    board.useMotherboard(ram);
    */
    
    RandomAccessMemory ram = new RandomAccessMemory();
    Motherboard board = new Motherboard();
    ram.useRAM(board);
    System.out.println();
    board.useMotherboard(ram);
  }
}

class RandomAccessMemory{
  
  void useRAM(Motherboard board){
    if(board == null)
      throw new NullPointerException("Can't use RAM because "
                +"there's no motherboard.");
    
    System.out.println("Motherboard found!");
    System.out.println("Plugging RAM into the motherboard...");
    System.out.println("RAM is plugged in!");
    System.out.println("Motherboard is available for use!");
  }
}

class Motherboard{

  void useMotherboard(RandomAccessMemory ram){
    if(ram == null)
      throw new NullPointerException("Can't use motherboard because "
                +"there's no RAM."); 
    
    System.out.println("RAM found!");
    System.out.println("Plugging RAM into the motherboard...");
    System.out.println("RAM is plugged in!");
    System.out.println("Motherboard is available for use!");
  }
}
In the example above, SampleClass possesses motherboard and RAM object references. RAM is dependent from motherboard in order to function and vice-versa. A motherboard can't function without RAM and vice versa.

Java OOPs: Abstraction

Chapters

Java Abstraction

Abstraction is the process of hiding intricate details of a system and only showing the essential parts. Operating System is an example of abstraction. When we use our computer, the operating system shows the user interface which users can interact with.

We don't need to know how the operating system creates the user interface in order to use it. When we open files, we don't need to know the mechanics of opening files in order to do that task and so on.

In java, we can achieve data abstraction by using abstract class or interface.
public class SampleClass{

  public static void main(String[]args){
  
    Windows win = new Windows();
    win.showUI();
    win.openFile();
    Linux linux = new Linux();
    linux.showUI();
    linux.openFile();
  }
}

interface OperatingSystem{

  void showUI();
  void openFile();
  
}

class Windows implements OperatingSystem{

  @Override
  public void showUI(){
    System.out.println("Showing Windows UI...");
    System.out.println("Task Done!");
  }
  @Override
  public void openFile(){
    System.out.println("Opening File Using Windows...");
    System.out.println("Task Done!");
  }
}

class Linux implements OperatingSystem{

  @Override
  public void showUI(){
    System.out.println("Showing Linux UI...");
    System.out.println("Operatiion Successful!");
  }
  
  @Override
  public void openFile(){
    System.out.println("Opening File Using Linux...");
    System.out.println("Operation Successful!");
  }
}
In the example above, we have two users(implementors), Windows and Linux. The users of the OperatingSystem interface ony need to know the method signatures. All other information about the overriden method in OperatingSystem interface is irrelevant.

Data abstraction increases the flexibility and simplicity of our source code.

Wednesday, June 16, 2021

Java OOPs: Polymorphism

Chapters

Java Polymorphism

Polymorphism simply means "many forms". In java, we can achieve polymorphism through method overloading and overriding. By using polymorphism, a method can have multiple forms with different functionalities.

There are two types of polymorphism in java: static(compile-time) polymorphism and dynamic(run-time) polymorphism.

Static Polymorphism And Static Binding

A method definition(form) and a call for that definition, which is determined at compile-time is called static polymorphism. In java, static polymorphism is achieved through method overloading. Moreover, a bind(connection) between overloaded method call and its definition is created during compile-time and that bind is called static binding.
Example

public class SampleClass{

  public static void main(String[]args){
  
    Increment inc = new Increment();
    
    System.out.println("Original Value: " + inc.getValue());
    //Calling increment() 1st form
    inc.increment();
    System.out.println("Incremented Value: " + inc.getValue());
    System.out.println();
    
    System.out.println("Original Value: " + inc.getValue());
    //Calling increment() 2nd form
    inc.increment(3);
    System.out.println("Incremented Value: " + inc.getValue());
    System.out.println();
    
     System.out.println("Original Value: " + inc.getValue());
    //Calling increment() 3rd form
    inc.increment(3,2);
    System.out.println("Incremented Value: " + inc.getValue());
    System.out.println();
  }
}

class Increment{
  private int value;
  
  //Form1
  public void increment(){
    value = ++value;
  }
  //Form2
  public void increment(int increase){
    value += increase;
  }
  //Form3
  public void increment(int increase,int incCount){
  
    for(int i = 0; i < incCount; i++)
      value += increase;
  }
  
  public int getValue(){ return value; }
}
When we compile the above example, java creates binds between increment() calls and their respective method definitions(forms). Thus, Overloaded forms are resolved at compile-time.

When we run the compiled version of the example above, Java won't need to create binds between increment() calls and their respective method definitions(forms) at run-time because it's already created during compilation process.

Dynamic Polymorphism And Dynamic Binding

A method definition(form) and a call for that definition, which is determined at run-time is called dynamic polymorphism. This process is also known as Dynamic Method Dispatch.

In java, dynamic polymorphism is achieved through method overriding. Moreover, a bind(connection) between overriding method call and its definition is created during run-time and that bind is called dynamic binding.
Example

public class SampleClass{

  public static void main(String[]args){
  
    Messenger1 m1 = new Messenger1();
    //Call Form1
    m1.displayMessage();
    
    Messenger2 m2 = new Messenger2();
    //Call Form2
    m2.displayMessage();
  }
}

abstract class Message{

  abstract void displayMessage();
}

class Messenger1 extends Message{
  
  //Form1
  @Override
  void displayMessage(){
    System.out.println("Message from Messenger1");
  }
}

class Messenger2 extends Message{

  //Form2
  @Override
  void displayMessage(){
    System.out.println("Message from Messenger2");
  }
}
If we compile the example above, the binds between overriding methods and the calls for them won't be created during compile time. Instead, it will be created once we run the compiled version of the example above.

Java OOPs: Encapsulation

Chapter

Java Encapsulation

Encapsulation is the process of wrapping data into a single unit. We can think of encapsulation as a multivitamins capsule. Multivitamins has lots of various vitamins packed in a single capsule.

By using encapsulation, we can achieve data hiding, as the name implies, data hiding is the process of hiding certain data. It can also help us manage and control data for security and flexibility.

Most of the time, we use setters and getters to handle data that comes in and out. Getters and setters are methods that set or get data. For convenience, we name those methods with a prefix "get" for getters and "set" for setters. This example demonstrates encapsulation.

//This class encapsulates main method
public class SampleClass{
   
  
  public static void main(String[]args){
  
   PersonnalInfo myInfo = new PersonnalInfo("B.Ghosts",
                              "123456","246810","Programming");
   System.out.println();
   
   System.out.println("Name: " + myInfo.getName());
   System.out.println("Getting Public Key...");
   System.out.println("Public Key: " + myInfo.getPublicKey());
   System.out.println("Getting Hobby...");
   System.out.println("Hobby: " + myInfo.getHobby());
   System.out.println();
   
   System.out.println("Setting new public key...");
   myInfo.setPublicKey("246810","654321");
   System.out.println("Getting Public Key...");
   System.out.println("Public Key: " + myInfo.getPublicKey());
   System.out.println("Setting hobby...");
   myInfo.setHobby("Playing Games");
   System.out.println("Hobby is set!");
   System.out.println("Getting Hobby...");
   System.out.println("Hobby: " + myInfo.getHobby());
   
  }
}

//This class encapsulates fields(variables),
//methods, etc.
final class PersonnalInfo{
  //If we don't want anyone to
  //directly access class fields
  //then make them private.
  private String name;
  private String publicKey;
  private String privateKey;
  private String hobby;
  
   PersonnalInfo(String name,String publicKey,String privateKey,
                 String hobby){
    
    System.out.println("Creating personnal information of "
                       + name);
    this.name = name;
    System.out.println("Locking public key...");
    this.publicKey = lockKey(publicKey);
    System.out.println("Locking private key...");
    this.privateKey = lockKey(privateKey);
    this.hobby = hobby;
    System.out.println(name + " personnal information has been"+
                       +" created!");
  }
  
  //getters
  public String getName(){ return name; }
  public String getPublicKey(){ return unlockKey(publicKey); }
  public String getHobby(){ return hobby; }
  
  //setters
  public void setPublicKey(String privateKey,String newKey){
   
    if(unlockKey(privateKey).equals(this.privateKey)){
      System.out.println("Private key is matched with"
                        +" the current private key!");
      System.out.println("Changing current public key with"
                         +" the new one...");
      publicKey = lockKey(newKey);
      System.out.println("Public key has been changed"+
                         " successfully!");
    }
    else
      System.out.println("Private key doesn't match the"
                         +" current private key.");
    
  }
  public void setHobby(String hobby){
    this.hobby = hobby;
  }
  
  
  //extra methods for securty
  private String lockKey(String key){
     System.out.println("Verifying Key...");
     System.out.println("Key Signature is valid!");
     System.out.println("Locking Key...");
     System.out.println("Key Locked!");
     return key;
  }
  
  private String unlockKey(String key){
    System.out.println("Unlocking Current Key...");
    System.out.println("Key Unlocked!");
    return key;
  }
}
In the above example, we can set or get data in the class by using getters and setters method. We can see that the variables are more secure because users can't just change the variable values directly. They are required to use the methods that the class provided in order to manipulate data in the class.

Tuesday, June 15, 2021

Java Tutorial: Interface

Chapters

Java Interface

Note: Understanding of inheritance is required to understand this topic.
If a class is the blueprint of an object then interface is the blueprint of a class. Some folks call interface as "meta-class". Like standard class, interface can use the this and super keyword. Interface is a special type of class in java. Interface, like abstract class, provides abstraction. Let's create an example to demonstrate interface.
public class SampleClass{

  public static void main(String[]args){
  
    MyTV myTV = new MyTV();
    myTV.power();
    myTV.adjustLight(10);
  }
}

//interface
interface Controls{
  //interface variables, they're
  //default public and final
  //public and final keywords
  //can be omitted in interface
  //variables
  int LIGHT_LVL = 100;
  boolean POWER_STATE = true;
  
  //interface methods, they're
  //default public and abstract
  //the abstract and public
  //keywords can be omitted in
  //interface methods
  void adjustLight(int level);
  void power();
}

//use the implements keyword to implement
//an interface to a class
class MyTV implements Controls{
  private int lightLvl;
  private boolean isOff;
  
  MyTV(){
    lightLvl = LIGHT_LVL;
    isOff = POWER_STATE;
  }
  
  @Override
  public void adjustLight(int level){
  
    if(isOff){
      System.out.println("Can't adjust brightness because "+
                         "TV is off...");
      return;
    }
  
    System.out.println("Current brightness: "
                       + lightLvl + "%");
    System.out.println("Adjusting brightness by: "
                       + level + "%");
    lightLvl -= level;
    if(lightLvl < 0)
       lightLvl = 0;
    
    System.out.println("Current brightness: "
                       + lightLvl + "%");
  }
  
  @Override
  public void power(){
    if(isOff){
      isOff = false;
      System.out.println("power is on...");
    }
    else{
      isOff = true;
      System.out.println("power is off...");
    }
      
  }
}
Variables that are declared in an interface are implicitly public,static and final; Methods that are declared in an interface are implicitly public and abstract. public,static and final can be omitted in interface variables; abstract and public can be omitted in interface methods.

Once a class implements an interface with abstract methods, that class needs to define those abstract methods. Don't forget to follow the rules of method overriding when overriding an abstract method.

Differences Between Abstract Class and Interface

Abstract class and interface are used to achieve abstraction. Though, these two have differences:

1.) Abstract class fields can be final or non-final at the discretion of programmers whereas interface fields are final by default.

2.) Abstract class fields can be static or non-static at the discretion of programmers whereas interface fields are static by default.

3.) Abstract class fields can have different access modifier whereas interface fields are public by default.

4.) abstract keyword can be omitted in abstract methods in interface whereas abstract methods in abstract class need to explicitly include the abstract keyword.

5.) Abstract class has constructor whereas interface doesn't.

6.) Interface can have default methods whereas abstract class can't.

When To Use Interface And Abstract Class

1.) If you want to achieve full abstraction where all methods are abstract or close to full abstraction then, it's preferrable to use interface due to its properties. If you want to achieve partial abstraction then, it's preferrable to use abstract class.

2.) You may wanna use interface if you're building a complex abstraction. We can take advatange of java's multiple inheritance support to interfaces to create a complex type of abstraction.

3.) If you want your fields to have different access modifiers or you want to mix final and non-final fields then, use abstract class.

4.) Use interface if you want an implementation that can be used by any class in your project.

Inheritance Between Interfaces

We can use the extends keyword to subclass an interface by another interface. However, unlike in classes, a parent interface can subclass multiple interfaces by using a comma-separated list.
public class SampleClass{

  public static void main(String[]args){
  }
}

interface SystemMenuDefaults{
  String SCAN_INTERLACE = "Interlace";
  String SCAN_PROGRESSIVE = "Progressive";
}

interface ScreenControls{

  void adjustResolution();
  void scanMode(String scanType);
}

//an interface extending one interface
interface PeripheralControls extends ScreenControls{

  void moveUpAlternative();
  void moveDownAlternative();
}

//an interface extending two interfaces
interface SystemMenu extends SystemMenuDefaults,PeripheralControls{

  void screenRelatedMenu(int option);
}

interface Controls extends SystemMenu{
  int LIGHT_LVL = 100;
  boolean POWER_STATE = true;
  
  void adjustLight(int level);
  void power();
}

//The implementing class must implement
//the abstract methods of parent interface
//and its sub interfaces
class MyTV implements Controls{
  
  @Override
  public void adjustLight(int level){}
  @Override
  public void power(){}
  @Override
  public void screenRelatedMenu(int option){}
  @Override
  public void moveUpAlternative(){}
  @Override
  public void moveDownAlternative(){}
  @Override
  public void adjustResolution(){}
  @Override
  public void scanMode(String scanType){}
  
}
In the above example, we see that an interface can extend multiple interfaces. If a class implements a sub interface with parent interfaces then the class must define the abstract methods of the sub interface and its parent interfaces.

Inheritance Between Class And Interface

If we want a class to inherit an interface we use the implements keyword. Java supports multiple inheritance in interface. Thus, a class can inherit multiple interfaces using the implements keyword.
public class SampleClass{

  public static void main(String[]args){
  
    MyTV myTV = new MyTV();
    myTV.power();
    myTV.adjustLight(10);
  }
}

interface ControlsDefaults{
  int LIGHT_LVL = 100;
  boolean POWER_STATE = true;
}
  
interface Controls{
  
  void adjustLight(int level);
  void power();
}

//class implementing multiple interfaces using
//comma-separated list
class MyTV implements ControlsDefaults,Controls{
  private int lightLvl;
  private boolean isOff;
  
  MyTV(){
    lightLvl = LIGHT_LVL;
    isOff = POWER_STATE;
  }
  
  @Override
  public void adjustLight(int level){
  
    if(isOff){
      System.out.println("Can't adjust brightness because "+
                         "TV is off...");
      return;
    }
  
    System.out.println("Current brightness: "
                       + lightLvl + "%");
    System.out.println("Adjusting brightness by: "
                       + level + "%");
    lightLvl -= level;
    if(lightLvl < 0)
       lightLvl = 0;
    
    System.out.println("Current brightness: "
                       + lightLvl + "%");
  }
  
  @Override
  public void power(){
    if(isOff){
      isOff = false;
      System.out.println("power is on...");
    }
    else{
      isOff = true;
      System.out.println("power is off...");
    }
      
  }
}
Note: A class can inherilt an interface but an interface can't inherit a class.

To resolve shadowing of variables between interfaces, we can add their interfaces names everytime we access them from the implementing class.
public class SampleClass{

  public static void main(String[]args){
    ClassA classA = new ClassA();
  }
  
}

interface InterfaceA{
  String NAME = "InterfaceA";
}

interface InterfaceB{
  String NAME = "InterfaceB";
}

class ClassA implements InterfaceA,InterfaceB{
  
  ClassA(){
    System.out.println(InterfaceA.NAME);
    System.out.println(InterfaceB.NAME);
  }
}
Ambiguous Inheritance

1.) If a super interface and its sub interface have the same method then, the implementing class only needs to override the identical methods once. This also applies to implementing class with multiple super interfaces with identical methods.
public class SampleClass{

  public static void main(String[]args){
    ClassA classA = new ClassA();
  }
}

interface InterfaceA{

  void message();
}

interface InterfaceB extends InterfaceA{

  void message();
}

class ClassA implements InterfaceB{

  @Override
  public void message(){
    System.out.println("ClassA");
  }
  
  ClassA(){
    message();
  }
}
2.) If the implementing class has super class, super interfaces and their super interfaces and they have identical methods then, the implementing class won't be required to override the methods in the interfaces instead, java will accept the method implementation of the super class, if there's an impelementation in the super class. If all identical methods are abstract then, the implementing class only needs to override those methods once.
public class SampleClass{

  public static void main(String[]args){
    ClassB classB = new ClassB();
  }
}

interface InterfaceA{

  void message();
}

interface InterfaceB extends InterfaceA{

  void message();
}

abstract class ClassA{

  public void message(){
    System.out.println("ClassA");
  }
}

class ClassB extends ClassA implements InterfaceB{
  
  ClassB(){
    message();
  }
}
If we override message() in ClassB then, ClassB only needs to override those message() methods once.
public class SampleClass{

  public static void main(String[]args){
    ClassB classB = new ClassB();
  }
}

interface InterfaceA{

  void message();
}

interface InterfaceB extends InterfaceA{

  void message();
}

abstract class ClassA{

  public void message(){
    System.out.println("ClassA");
  }
}

class ClassB extends ClassA implements InterfaceB{
  
  @Override
  public void message(){
    System.out.println("ClassB");
  }
  
  ClassB(){
    super.message();
    message();
  }
}
3.) If the implementing class has super class, super interfaces and their super interfaces and they have similar methods with same names, same number of parameters but different return type then, A compile-time error will occur in the implementing class because implementing class can't override those methods because overriding those methods in one class is very similar to method overloading.
public class SampleClass{

  public static void main(String[]args){
  }
}

interface InterfaceA{

  void message();
}

abstract class ClassA{
  //comment this method if you're gonna use
  //the fix
  abstract String message();
  
  //1.) Try this method to fix the error
  //abstract String message(String message);
}

class ClassB extends ClassA implements InterfaceA{

  @Override
  public void message(){}
  
  //comment this method definition if you're gonna use
  //the fix
  @Override
  String message(){return "";}
  
  /*2.)
  @Override
  String message(String message){ return message; }
  */
}
The example above will throw a compile-time error. To fix the error, we can add a parameter to one of the message() methods to comply with the method overloading rules.

Tag or Marker Interface

As the name implies, tag or marker interface tags of marks a class. Java has pre-defined marker interfaces like Serializable, EventListener,etc. These interfaces have special meaning to the JVM. For example, if we tag a class as serializable then the instances of that class can be serialize in a file. Take a look at this example:
import java.io.Serializable;

public class SampleClass{

  public static void main(String[]args){
  }
}

//that instances of this class can be serialize
class ClassA implements Serializable{
  String name;
  int num;
}
We can create our own marker interface if we wanna categorize our classes.
public class SampleClass{

  public static void main(String[]args){
  
    Rectangle rect = new Rectangle();
    
    //check if a shape is non-intersecting
    if(rect instanceof NonIntersecting)
      System.out.println("Rectangle has no intersecting "
                         +"line segments");
  }
}

//tag or marker interface
interface NonIntersecting{}

//tag this class as non-intersecting shape
class Rectangle implements NonIntersecting{
}

//tag this class as non-intersecting shape
class Triangle implements NonIntersecting{
}

//Pentagram has intersecting line segments
class Pentagram{
}

Functional Interface

This topic is pretty advance and an understanding of lambda expression is required to understand the use of this interface. Regardless, I'm gonna explain the basic concept of this interface very briefly. A functional interface is an interface with only one abstract method. However, it can have multiple default and static methods. default method is an added feature in JDK8.

This example shows the structure of a functional interface.
//@FunctionalInterface is an annotation
@FunctionalInterface
interface MyFunctionalInterface{

  void commit();
  
  //this method is optional in
  //functional interface
  default void greetings(){
    System.out.println("Hello! This is "+
                       "my functional interface");
  }
}
Java has pre-defined functional interfaces that we can use. Check out the java.util.function package. An understanding of generics is required to understand the syntax of the functional interfaces in that package.

Calling Method Using Interface Reference

We know that interface can't be instantiated. However, just like abstract class, typecasting can still happen between a sub class and a super interface.
public class SampleClass{

  public static void main(String[]args){
  
    Messenger messenger = new Messenger();
    
    //create the interface reference and upcast
    //NotificationSender instance
    Notifications notif = new NotificationSender();
    
    //these methods only need messageNotification()
    //and alertNotification methods. So, using 
    //Notification reference will suffice here and
    //avoid the misuse of displayNotificationCount().
    messenger.sendMessage("Hi There!",notif);
    messenger.sendAlert("You Battery is low!",notif);
    
    //downcast the NotificationSender instance that is
    //referenced in notif variable in order to access
    //the displayNotificationCount() method.
    NotificationSender ns = (NotificationSender)notif;
    ns.displayNotificationCount();
    
  }
}

interface Notifications{

  void messageNotification();
  void alertNotification();
}

class NotificationSender implements Notifications{
  private int msgNotifCount;
  private int alertNotifCount;

  @Override
  public void messageNotification(){
    System.out.println("Message Sent!");
    msgNotifCount++;
  }
  
  @Override
  public void alertNotification(){
    System.out.println("Alert! This matter is "
                       +"somewhat important!");
    alertNotifCount++;
  }
  
  public void displayNotificationCount(){
    
    System.out.println("message notifation count: "
                       +msgNotifCount);
    System.out.println("alert notifation count: "
                       +alertNotifCount);
  }
}

class Messenger{

  void sendMessage(String message,Notifications notif){
    System.out.println(message);
    
    //call messageNotification() using the
    //reference of Notifications interface
    notif.messageNotification();
  }
  
  void sendAlert(String alert,Notifications notif){
    System.out.println(alert);
    
    //call alertNotification() using the
    //reference of Notifications interface
    notif.alertNotification();
  }
}
Static Class In Interface

An interface can't have an inner class due to the fact that it doesn't have contructor. Although, interface can have static nested class as its member. Classes that are defined in interface are implicitly static.
public class SampleClass{

  public static void main(String[]args){
  
    MyTV myTV = new MyTV();
    myTV.adjustLight();
    myTV.adjustVolume();
    
    if(myTV.toggleAltControls())
      myTV.useAltControls(Controls.VOLUME);
    
  }
}

interface Controls{
  String LIGHT = "light";
  String VOLUME = "volume";
  
  
  //class member of interface
  //is static by default
  //static keyword can be omitted
  class PeripheralControls{
    
    void adjustVolumeAlternative(){
      System.out.println("Alternative Volume "+
                         "Controller");
    }
    
    void adjustLightAlternative(){
      System.out.println("Alternative Light "+
                         "Controller");
    }
    
  }
  
  void adjustLight();
  void adjustVolume();
}

class MyTV implements Controls{
  private PeripheralControls perpCon;
  private boolean isActivated;
  
  MyTV(){
    isActivated = false;
    perpCon = new PeripheralControls();
  }
  
  @Override
  public void adjustLight(){
    System.out.println("Main Light "+
                       "Controller");
  }
  
  @Override
  public void adjustVolume(){
    System.out.println("Main Volume "+
                       "Controller");
  }
  
  boolean toggleAltControls(){
    isActivated = !isActivated;
    return isActivated;
  }
  
  void useAltControls(String control){
  
    if(isActivated){
    
      switch(control){
      
        case Controls.LIGHT:
        perpCon.adjustLightAlternative();
        break;
        
        case Controls.VOLUME:
        perpCon.adjustVolumeAlternative();
        break;
        
        default:
        System.out.println("Invalid Input.");
        break;
        
      }
    }
    else System.out.println("Can't use "
                    +"alternative controls "
                    +"because it's not activated");
  }
  
}

Added features In Interface since Java 8

Since Java 8, there are added features that improve java interface. Those improvements really pushed interface boundary making it more flexible and usable. These are the added features:

Default Method

Default method also called defender method or virtual extension method was introduced in java8. Prior to java8, interface can't have defined methods.

However, having no defined methods in interface pose a problem. For example, if an interface has been implemented multiple times in a project and then we want to add another method; We have no choice but to implement that additional method to all implementing classes of that interface.

First of all, that's gonna be tedious and a frustrating work. One of the main purposes of default methods is to provide a simple way of adding new methods without updating the implementing classes. Let's take a look at this example.
public class SampleClass{

  public static void main(String[]args){
  
  Message1 msg1 = new Message1();
  if(msg1.message(null) == false)
    msg1.defaultMessage();
  
  Message2 msg2 = new Message2();
  if(msg2.message(null) == false)
    msg2.defaultMessage();
  
  }
}

interface DefaultMessage{
  
  //default method is not required to be overriden
  //we can override this if it's needed
  default void defaultMessage(){
    System.out.println("Default message");
  }
  
  boolean message(String message);
}

class Message1 implements DefaultMessage{
  
  //Overriding the default method
  @Override
  public void defaultMessage(){
    System.out.println("Default message in Message1");
  }
  
  @Override
  public boolean message(String message){
  
    if(message == null)
      return false;
  
    System.out.println("Message1 message: "
                       +message);
    return true;
  }
}

class Message2 implements DefaultMessage{

  @Override
  public boolean message(String message){
    if(message == null)
      return false;
  
    System.out.println("Message2 message: "
                       +message);
    return true;
  }
}
In the above example, we see that we're the implementing classes are not required to override the default method. Thus, implementing classes are not required to update everytime we add default methods in our interface. Also, default method is public by default.

If we want to access identical default methods between interfaces, we need to override the identical methods first then we can use the super keyword to call for the methods.
public class SampleClass{

  public static void main(String[]args){
  
    ClassA classA = new ClassA();
  }
}

interface InterfaceA{

  default void message(){
    System.out.println("InterfaceA");
  }
}

interface InterfaceB{

  default void message(){
    System.out.println("InterfaceB");
  }
}

class ClassA implements InterfaceA,InterfaceB{
  
  //Override message() first
  @Override
  public void message(){
    //call default methods using the super
    //keyword
    InterfaceA.super.message();
    InterfaceB.super.message();
  }
  
  ClassA(){
    message();
  }
}
If you don't override message() first, you will encounter this error:
"ClassA inherits unrelated defaults..."

Default method can be redeclared as abstract method in another interface.
public class SampleClass{

  public static void main(String[]args){
  
    ClassA classA = new ClassA();
  }
}

interface InterfaceA{

  //default method
  default void message(){
    System.out.println("InterfaceA");
  }
}

interface InterfaceB extends InterfaceA{
  
  //redeclare default method as abstract method
  void message();
}

class ClassA implements InterfaceB{
  
  //redifine message() in implementing class
  @Override
  public void message(){
    System.out.println("ClassA");
  }
  
  ClassA(){
    message();
  }
}
Default method has this "static-non-static" behaviour, if you will. Default method can only access static class/interface members. However, default method can't be called like static method. Take a look at this example:
public class SampleClass{

  public static void main(String[]args){
    
    //error: can't call getName() default method directly
    //System.out.println(ClassA.Interface1.getName());
    
    new ClassB();
    
    //static method can be called directly
    ClassA.Interface1.setName("main");
    
    new ClassB();
  }
}

abstract class ClassA{
  //this variable can be accessed by
  //default method in nested interface
  //because it's static
  private static String name = "ClassA";
  
  //nested interface
  interface Interface1{
  
    default String getName(){
      return name;
    }
    
    static void setName(String name){
      ClassA.name = name;
    }
  }
  
}

//implement Interface1 in ClassA
class ClassB implements ClassA.Interface1{
  
  ClassB(){
    //default method can be called in
    //implementing class
    System.out.println(getName());
  }
}
Private and Static Method

Interface can now have private and static methods. A non-private static method is implicitly public.
public class SampleClass{

  public static void main(String[]args){
    Defaults.defaultMessage();
  }
}

interface Defaults{
  
  //private static method
  private static void defaultFont(){
    System.out.println("Default Font Acquired!");
  }
  
  //implicitly public
  static void defaultMessage(){
    System.out.println("Acquiring Default Font...");
    defaultFont();
    System.out.println("This is a Default Message!");
  }
  
}

Nested Interface

Like classes, interface can also be nested. An interface can be nested in a class or another interface. One of the uses of nested interface is to group interfaces that are related to each other.
Interface In Another Interface

An interface can have interface as its members. A nested interface in another interface is implicilty public.
public class SampleClass{

  public static void main(String[]args){
    String toolToUse = Toolkit.HAMMER;
    
    switch(toolToUse){
    
      case Toolkit.HAMMER:
      Toolkit.ConstructionTools.useHammer();
      break;
      
      case Toolkit.SCREWDRIVER:
      Toolkit.HardwareTools.useScrewDriver();
      break;
    }
    
  }
}

interface Toolkit{
  String HAMMER = "hammer";
  String NAILS = "nails";
  String SCREWDRIVER = "screwdriver";
  String SCREWS = "screws";
  
  //nested interface
  interface ConstructionTools{
  
    static void useHammer(){
      getNails();
      System.out.println("Using " + HAMMER + "...");
      System.out.println("Job's Done!");
    }
    
    private static void getNails(){
      System.out.println("Getting nails...");
      System.out.println(NAILS+" acquired!");
    }
  }
  
  //nested interface
  interface HardwareTools{
    static void useScrewDriver(){
      getScrews();
      System.out.println("Using " + SCREWDRIVER + "...");
      System.out.println("Job's Done!");
    }
    
    private static void getScrews(){
      System.out.println("Getting screws...");
      System.out.println(SCREWS+" acquired!");
    }
  }
}
Here's another example:
public class SampleClass{

  public static void main(String[]args){
    MyConstTools mct = new MyConstTools();
    mct.useHammer();
    mct.useDrill();
    
    MyCustomTools cTools = new MyCustomTools();
    cTools.useTool();
    
  }
}

interface Tools{
  
  interface ConstructionTools{
    
    void useHammer();
    void useDrill();
  }
  
  void useTool();
}

//implement nested interface
class MyConstTools implements Tools.ConstructionTools{

  @Override
  public void useHammer(){
    System.out.println("Using my hammer...");
  }
  
  @Override
  public void useDrill(){
    System.out.println("Using my drill...");
  }
  
}

//implement top-level interface
class MyCustomTools implements Tools{

  @Override
  public void useTool(){
    System.out.println("Using my cutom-made tool");
  }
}
Interface In a Class

An interface can be nested in a class. Unlike interface in another interface, interface in a class can have public, default or protected access modifier.
public class SampleClass{

  public static void main(String[]args){
  
    MyToolBox toolBox = new MyToolBox(ToolBox.
                                      WoodenBox.BOX_NAME,
                                      7);
                        
    if(ToolBox.WoodenBox.checkBox(toolBox))
      System.out.println("This box is a: " +
                         ToolBox.WoodenBox.BOX_NAME);
    else if(ToolBox.MetalBox.checkBox(toolBox))
      System.out.println("This box is a: " +
                         ToolBox.MetalBox.BOX_NAME);
                         
  }
}

abstract class ToolBox{
  private static String name;
  
  ToolBox(String name){
  
    this.name = name;
  }
  
  //nested interface
  interface WoodenBox{
    String BOX_NAME = "Wooden Box";
    
    static boolean checkBox(ToolBox box){
      //interface can only access static
      //members of a class
      if(box.getName() == name)
        return true;
      else return false;
      
    }
  }
  
  //nested interface
  interface MetalBox{
    String BOX_NAME = "Metal Box";
    
    static boolean checkBox(ToolBox box){
      //interface can only access static
      //members of a class
      if(box.name == name)
        return true;
      else
        return false;
    }
  }
  
  String getName(){ return name; }
  
}

class MyToolBox extends ToolBox{
  private int weight;
  
  MyToolBox(String name,int weight){
    super(name);
    this.weight = weight;
  }
}
In the above example, we see that interface can access the name static variable in ToolBox. Remember, nested interface in a class can access static members but not instance members because nested interface behave similarly to nested static class.

Sunday, June 13, 2021

Java Tutorial: The final Keyword

Chapters

The final Keyword

The final keyword can be used in a variable, method and class. The final keyword affects variable, method and class differently.

Using final Keyword In Variables

When we use final keyword in a variable, the value of the variable can't be changed. We call these variables as constants. If the value of a variable is an object reference then the state of the object of the reference can be changed but the reference that is assigned with the variable can't be changed. This property is called non-transitivity.

arrays and collections have this property because arrays and collections are objects. Constant variables require less resources than regular variables. Thus, constants may improve the performance of our program.

Let's create an example to demonstrate final variables.
public class SampleClass{
  //blank final instance variable
  //this variable needs to
  //be instantiated in an
  //initializer block or
  //a constructor
  final String NAME;
  
  //final instance variable
  static final int ONES = 5;
  
  //final instance variable
  //with object reference
  final StringBuilder STR_BUILD = 
                  new StringBuilder("abcd");
  
  //blank final static variable
  //this variable needs to be
  //instantiated in a static
  //block
  //
  //Sometimes, it's convenient to make your
  //final variable static to prevent
  //it from having duplicates
  //everytime we create a class
  //object where the final variable
  //resides
  static final double DECIMAL;
  
  //final static variable
  final static int TENS = 37;
  
  SampleClass(){
    //initialize blank final
    //in a constructor or
    //initializer block
    NAME = "SampleClass";
    
    //error: once blank final is 
    //initialized, we can't
    //change its value anymore
    //NAME = "ClassSample";
  }
  
  static{
    //initialize static blank final
    //in a static block
    DECIMAL = 10.5;
    
    //error: once blank final is 
    //initialized, we can't
    //change its value anymore
    //DECIMAL = 11;
  }
  
  public static void main(String[]args){
    //final local variable
    final int NUM = 1;
    
    //error. can't change num value because
    //it's final
    //NUM = 5;
    
    SampleClass sc = new SampleClass();
    System.out.println(sc.NAME);
    System.out.println(sc.ONES);
    System.out.println(sc.TENS);
    System.out.println(sc.DECIMAL);
    System.out.println();
    
    System.out.println(sc.STR_BUILD.toString());
    System.out.println("Add String...");
    
    //change object state
    sc.STR_BUILD.append("efgh");
    
    //error: reference assigned to builder variable
    //can't be changed
    //sc.STR_BUILD = new StringBuilder("abcdefgh");
    
    System.out.println(sc.STR_BUILD.toString());
  }
}
There are lots of interesting things going on in the example. We see that once a final variable has been initialized, its value can't be changed. If the variable is a reference type variable, the properties of the object referenced by the reference can be changed but the reference that the variable holds can't be changed.

Now, let's talk about blank final variable. This variable can be declared without a value. However, it's required to be initialized in an initializer block or constructor if the variable is non-static, if it's static then it's required to be initialized in a static block.

Using final Keyword In Classes

When final keyword is used in classes, those classes can't be extended or in other words, they can't have child classes.
Note: final keyword doesn't work with abstract classes.
public class SampleClass{

  public static void main(String[]args){
  }
}

final class ClassA{
  static String name = "ClassA";

}

//error: Class can't extend ClassA
class ClassB extends ClassA{
  String name = "ClassB";
}
Using final Keyword In Methods

When final keyword is used in methods, those methods can't be overriden.
public class SampleClass{

  public static void main(String[]args){
  }
}

class ClassA{
  static String name = "ClassA";
  
  //final method
  public final void displayName(){
    System.out.println(name);
  }
  
  //This syntax is legal but final keyword
  //here is redundant since static methods
  //can't be overriden already
  /*
  public final static void displayName(){
    System.out.println(name);
  }
  */
  
}

class ClassB extends ClassA{
  static String name = "ClassB";
  
  //error: can't override final
  //method
  /*
  @Override
  public final void displayName(){
    System.out.println(name);
  }
  */
  
  //error: can't override static method
  /*
  @Override
  final static public void displayName(){
    System.out.println(name);
  }
  */
  
  public void displayParentName(){
    super.displayName();
  }
}
In the above example, we see that displayName() in ClassA can't be overriden. We also see that adding final keyword in a static method is redundant since static methods can't be overriden already.

Another interesting thing is this syntax:
public final static void displayName()
In this syntax we have three modifiers: public access modifier, static and final. Some modifiers can be arranged in any order. These three modifiers can be placed in any order
Example
static public final void displayName(){}

Using final Keyword In Method/Constructor Parameter

When final keyword is used in Method/Constructor Parameter, the value of the parameter can't be changed in the method/constructor block. Otherwise, a compilation error will be thrown.
public class SampleClass{
  
  static void method(final String str){
    String myString = str;
    
    //Uncomment this code below
    //and you will encounter a compilation
    //error
    //str = "This is an error";
  }
  
  public static void main(String[] args){
  
  }
}