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.

No comments:

Post a Comment