Chapter 1. Lambda Expressions

1.1.  Describe and develop code that uses Java inner classes, including nested class, static class, local class, and anonymous classes

[Note]

There are four different types of inner classes you can use in Java. The following gives their name and examples:

Static Nested Classes

public class Outer {
    static class Inner {
        void doIt() {
            System.out.print("Static nested class reference is: " + this);
        }
    }
}

class Test {
    public static void main(String[] args) {
        Outer.Inner n = new Outer.Inner();
        n.doIt();
    }
}
					

  • Similar to inner class, but declared as static class.

  • No link to an instance of the outer class.

  • Can only access static fields and methods of the outer class.

  • Useful if inner class object:

    • Associated with different outer class objects.

    • Survives longer than outer class object.

Member Inner Class

Member class is instance-specific (i.e. it's declared at instance level members - right after outer class declaration.)

It has access to all methods, fields, and the Outer's this reference:

public class Outer {

    private int secretVar = 1;

    public void makeInner() {
        Inner in = new Inner();
        in.seeOuter();
    }

    class Inner {
        public void seeOuter() {
            System.out.println("Outer 'secretVar' is " + secretVar);
            System.out.println("Inner class reference is " + this);
            System.out.print("Outer class reference is " + Outer.this);
        }
    }

    public static void main(String... args) {
        Inner i = new Outer().new Inner();
        i.seeOuter();
    }
}
					

  • Class defined in scope of another ("outer") class.

  • May be named or anonymous (see the next section for anonymous inner classes details.)

  • Outer and inner class can directly access each other's fields and methods (even if private.)

  • Inside methods of outer class, use inner class as any other class Inner i = new Inner();

  • Inner class instance has association to an instance of outer class.

  • Inner class instance must be instantiated with an enclosing instance.

  • Inner class instance is tied to outer class object at moment of creation (can not be changed.)

Method-Local Inner Classes

Local inner classes are classes that are defined in a block, which is a group of zero or more statements between balanced curly braces. You typically find local classes defined in the body of a method:

Instance method local inner class:

public class Outer {

    private String x = "outer";

    public void doStuff() {

        class MyInner {
            public void seeOuter() {
                System.out.println("x is " + x);
            }
        }

        MyInner i = new MyInner();
        i.seeOuter();
    }

    public static void main(String[] args) {
        Outer o = new Outer();
        o.doStuff();
    }
}
					

Static method local inner class:

public class Outer {

    private static String x = "static outer";

    public static void doStuff() {

        class MyInner {
            public void seeOuter() {
                System.out.println("x is " + x);
            }
        }

        MyInner i = new MyInner();
        i.seeOuter();
    }

    public static void main(String[] args) {
        Outer.doStuff();
    }
}
					

A local inner class has access to the members of its enclosing class.

In addition, a local class has access to local variables. In Java SE 7 and earlier, a local class can ONLY access local variables that are declared final. When a local class accesses a local variable or parameter of the enclosing block, it captures that variable or parameter:

// Java SE 7 syntax
...
public void validatePhoneNumber(String phoneNumber) {

    final int numberLength = 10;

    class PhoneNumber {
        PhoneNumber(String phoneNumber) {
            if (phoneNumber.length() > numberLength) { ... }
        }
    }
}
...
					

[Warning]

Starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final. For example, suppose that the variable numberLength is not declared final:

// Java SE 8 - successfully compiles and runs;
// Java SE 7 - compilation failure
...
public void validatePhoneNumber(String phoneNumber) {

    int numberLength = 10; // no mandatory 'final' anymore, as long as variable not modified

    class PhoneNumber {
        PhoneNumber(String phoneNumber) {
            if (phoneNumber.length() > numberLength) { ... }
        }
    }
}
...
					

[Warning]

In the following example because of numberLength assignment statement inside PhoneNumber class, the variable numberLength is not effectively final anymore. As a result, the Java compiler generates an error message similar to "Local variables referenced from an inner class must be final or effectively final":

// Java SE 8 - compilation failure
...
public void validatePhoneNumber(String phoneNumber) {

    int numberLength = 10;

    class PhoneNumber {

        numberLength = 12;

        PhoneNumber(String phoneNumber) {
            if (phoneNumber.length() > numberLength) { ... }
        }
    }
}
...
					

Anonymous Inner Classes

This is frequently used when you add an action listener to a widget in a GUI application:

public class Outer {

    public void init() {
        JButton button = new JButton();
        JLabel comp = new JLabel();

        button.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                comp.setText("Button has been clicked");
            }
        });
    }
}
					

  • Inner class is without class name.

  • Defined where you create an instance of it (in the middle of a method.)

  • Useful if the only thing you want to do with an inner class is create instances of it in one location.

  • Syntax:

    SomeType x = new SomeType() {     // unnamed (i.e. anonymous) inner class
        // body of class goes here
    };
    								

    where SomeType must be existing class or interface.

    If SomeType is a class - inner anonymous class extends provided class.

    If SomeType is an interface - inner anonymous class implements the provided interface.

Shadowing

If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope. You cannot refer to a shadowed declaration by its name alone. The following example, Shadow, demonstrates this:

public class Shadow {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("Shadow.this.x = " + Shadow.this.x);
        }
    }

    public static void main(String... args) {
        Shadow s = new Shadow();
        Shadow.FirstLevel fl = s.new FirstLevel();
        fl.methodInFirstLevel(2);
    }
}
					

The following is the output of this example:

x = 2
this.x = 1
Shadow.this.x = 0
					

This example defines three variables named x: the member variable of the class Shadow, the member variable of the inner class FirstLevel, and the parameter in the method methodInFirstLevel. The variable x defined as a parameter of the method methodInFirstLevel shadows the variable of the inner class FirstLevel. Consequently, when you use the variable x in the method methodInFirstLevel, it refers to the method parameter. To refer to the member variable of the inner class FirstLevel, use the keyword this to represent the enclosing scope:

System.out.println("this.x = " + this.x);
					

Refer to member variables that enclose larger scopes by the class name to which they belong. For example, the following statement accesses the member variable of the class Shadow from the method methodInFirstLevel:

System.out.println("Shadow.this.x = " + Shadow.this.x);
					

Professional hosting         Free 'Oracle Certified Expert Web Services Developer 6' Guide     Exam 1Z0-810: Upgrade to Java SE 8 Programmer Quiz