Nested Classes

Inner Class

  • Inner class is associated with an instance of its enclosing class
  • It has direct access to that object’s methods and fields

Local Class

  • They are defined in a block which is typically method/for-loop/if-clause etc.
  • Local class can only access local variables that are declared final or effectively final
  • When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter
public class LocalClassExample {
    static String regularExpression = "[^0-9]";
    final int numberLength = 10; // captured variable
 
    public static void validatePhoneNumber(String p1, String p2) {
        
        class PhoneNumber {
            String formattedPhoneNumber = null;
            PhoneNumber(String phoneNumber) {
                String currentNumber = phoneNumber.replaceAll(
                        regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }
 
            public String getNumber() {
                return formattedPhoneNumber;
            }
        }
 
        PhoneNumber myNumber1 = new PhoneNumber(p1);
        PhoneNumber myNumber2 = new PhoneNumber(p2);
 
        if (myNumber1.getNumber() == null)
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());
 
    }
 
    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
        // First number is 1234567890  
        // Second number is invalid
    }
}

Anonymous Class

  • https://www.baeldung.com/java-anonymous-classes
  • They enable you to declare and instantiate a class at the same time
  • They are local classes but without name
  • Syntax
    • The new operator
    • Name of the interface to implement or class to extend
    • Parentheses that contain the arguments to a constructor
    • Class declaration body
new Interface() {
    // body
}
 
new ParentClass(arg1, arg2, ...) {
    // body
}
  • Examples
interface HelloWorld {
    void greet();
    void greetSomeone(String someone);
}
 
abstract class Book {
    protected final String name;
    Book(String name) {
        this.name = name;
    }
    abstract String description();
}
 
public class Test {
    public static void main(String... args) {
        HelloWorld frenchGreeting = new HelloWorld() {
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };
        frenchGreeting.greet();
        
        Book book = new Book("Harry Potter") {
            @Override
            String description() {
                return "Famous Novel: " + this.name;
            }
        };
 
        System.out.println(book.description());
    }
}
  • Like local classes, anonymous classes can capture variables
  • You cannot declare static initializers or member interfaces in an anonymous class
  • An anonymous class cannot have an explicitly declared constructor
  • You can only declare:
    • Constants (static final fields)
    • Instance Fields
    • Overriding methods
    • Extra methods
    • Instance initializers
    • Local classes
  • Uses
    • Event Listeners in UI

Static Nested Class

  • Static nested class is associated with its outer class
  • It cannot refer directly to instance variables or methods defined in its enclosing class
  • In effect, a static nested class is behaviorally a top-level class that has been nested in another top-level class for packaging convenience
// TopLevelClass.java
public class TopLevelClass {
 
    void accessMembers(OuterClass outer) {     
        // Compiler error: OuterClass.outerField is not static
        // System.out.println(OuterClass.outerField);
        System.out.println(outer.outerField);
        System.out.println(OuterClass.staticOuterField);
    }  
}
 
// OuterClass.java
public class OuterClass {
    String outerField = "Outer field";
    static String staticOuterField = "Static outer field";
 
    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }
 
    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            // Compiler error: field outerField is not static
            // System.out.println(outerField);
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }
    
    public static void main(String[] args) {
        System.out.println("---Inner class---");
        OuterClass outerObject = new OuterClass();
        OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        innerObject.accessMembers();
 
        System.out.println("---Static nested class---");
        StaticNestedClass staticNestedObject = new StaticNestedClass();
        staticNestedObject.accessMembers(outerObject);
 
        System.out.println("---Top-level class---");
        TopLevelClass topLevelObject = new TopLevelClass();        
        topLevelObject.accessMembers(outerObject);                
    }
}