Inner Classes

Author: Tatyana Milkina

An inner class is a "class within a class" in java. An instance of an inner class has a "special relationship" with an instance of the outer class. That "special relationship" gives code in the inner class access to members of the enclosing (outer) class as if the inner class were part of the outer class. 

There are four types of inner class:

  • Regular inner class (or member inner class)
  • Method-local inner class
  • Anonymous inner class
  • Static nested class

1. Regular Inner Class

  • An inner class is defined within the curly braces of the outer class.
  • To create an instance of the inner class, an instance of the outer class should exist.
  • Inner and outer classes have the access to each other's members (even private).

Example 1.1. Regular Inner Class

The inner class Street is defined within outer class Town.

class Town {
    private String postCode = "33333";

    public void createStreet() {
        Street street = new Street();
        street.printAddress();
    }

    class Street {
        public void printAddress() {
            System.out.println("Town is " + Town.this);
            System.out.println("PostCode is " + postCode);
            System.out.println("Street is " + this);

        }
    }

    public static void main(String[] args) {
        Town town = new Town();
        town.createStreet();
        Street street1 = town.new Street();
        Street street2 = new Town().new Street();
        street1.printAddress();
        street2.printAddress();
    }
}
  • Inside methods of the outer class, an inner class can be used as any other class:
  • Street street = new Street();
  • From outside the outer class instance code (including static method code within the outer class), the inner class name must include the outer class's name:
    Town.Street
  • Use a reference to the outer class or an instance of the outer class, to instantiate an inner class:

    new Town().new Street(); or town.new Street();
  • To reference the inner class instance itself, from within the inner class code, use "this".
  • To reference the outer class instance from within the inner class code, use "Town.this".
  • A regular inner class is a member of the outer class just as instance variables and methods are, so the following modifiers can be applied to an inner class:
    • final
    • abstract
    • public
    • private
    • protected
    • static - but static turns it into a static nested class, not an inner class
    • strictfp

2. Method-Local Inner Class

Let's see what is method local inner class in java:

  • A method-local inner class is defined within a block, usually within a method.
  • An instance of a method-local inner class should be created within the same method but below the class definition.
  • This type of inner class shares a "special relationship" with the outer class object, and can access all its members (even private).
  • A method-local inner class can have only those modifiers, which are applied to local variable declarations. It cannot be marked as public, private, protected, static, transient. A method-local inner class can be abstract or final.

Example 2.1. Method-Local Inner Class

class Town {
    private String postCode = "33333";

    public void createAddress() {
        final int houseNumber = 34;
        class Street {
            public void printAddress() {
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }
    public static void main(String[] args) {
        Town town = new Town();
        town.createAddress();
    }
}

A local class declared in a static method has access to only static members of the enclosing class.

Example 2.2. Static Method-Local Inner Class

class Town {
    private static String postCode = "33333";

    public static void createAddress() {
        final int houseNumber = 34;
        class Street {
            public void printAddress() {
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }

    public static void main(String[] args) {
        Town.createAddress();
    }
}

In Java SE 7 and earlier, the inner class object cannot use the local variables of the method of the inner class, unless the local variables are marked final. In Example 2.1, the variable "houseNumber" is marked as final.

Starting from 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. 

Example 2.3. Effectively final local Variable

Let's update the code from Example 2.1, making the local variable "houseNumber" of the createAddress() method effectively final instead of final:

// Java SE 8 - successfully compiles and runs;
// Java SE 7 - compilation failure
...      
    public void createAddress() {
        int houseNumber = 34;
        class Street {
            public void printAddress() {
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }
...

Example 2.4. Change Local Variable

In this example, we are trying to change the local variable "houseNumber". As a result, the variable "houseNumber" is not effectively final anymore, and the Java compiler generates an error message "Local variables referenced from an inner class must be final or effectively final":

// Java SE 8 - compilation failure
...
    public void createAddress() {
        int houseNumber = 34;
        class Street {
            public void printAddress() {
                houseNumber = 78;
                System.out.println("PostCode is " + postCode);
                System.out.println("House Number is " + houseNumber);
            }
        }
        Street street = new Street();
        street.printAddress();
    }
...

3. Anonymous Inner Class

Let's discuss what is an anonymous inner class in java:

  • Anonymous inner class is declared without any class name.
  • These classes can be defined not just within a method, but even within an argument to a method.
  • They are useful if the only thing it is required to do with the inner class is creating instances of it in one location.

Example 3.1. Syntax of an anonymous inner class

The example shows the syntax of a simple anonymous inner class, where AnyType is a class or an interface.

AnyType a = new AnyType(){
     //body of the class
};

It is possible to call only those methods of an anonymous class reference, which are defined in the reference variable type.

Example 3.2. Anonymous Inner Class defines a new Method

A class Potato has only one method - peel(). An anonymous inner class, which extends Potato, defines its own method fry() - this is eligible. But when we are trying to invoke this method outside an anonymous inner class - in the method prepare(), the compiler generates an error.

class Potato {
    public void peel() {
        System.out.println("peeling potato");
    }
}

class Food {
    Potato p = new Potato() {
        public void fry() {
            System.out.println("frying potato anonymously ");
        }

        public void peel() {
            System.out.println("peeling potato anonymously");
        }
    };

    public void prepare() {
        p.peel();
        p.fry(); //compilation failure
    }
}

An anonymous inner class can not only extend another class but implement an interface as well (but only one interface). Let's look at the next example:

Example 3.3. Anonymous Inner Class implements an Interface

interface Cookable {
    public void cook();
}

class Food {
    Cookable c = new Cookable() {
        public void cook() {
            System.out.println("anonymous cookable implementer");
        }
    };
}

Example 3.4. Anonymous Inner Class, defined as a Method Argument

 The example shows how to define an anonymous inner class as a method parameter:

class Food {
    public void prepare(Cookable c) {
        c.cook();
    }

    public static void main(String[] args) {
        Food food = new Food();
        food.prepare(new Cookable() {
            public void cook() {
                System.out.println("Preparing something yummy.");
            }
        });
    }
}

interface Cookable {
    void cook();
}

4. Static Nested Class

A static nested class in java is often called a static inner class, but it isn't an inner class at all by the standard definition of an inner class. While any inner class has the "special relationship" with the outer class, a static nested class does not.

A static nested class doesn't have access to non-static members of the outer class.

Example 4.1. Syntax of Static Nested Class

Let's look at the simple definition of the static nested class. The static modifier in this case means that the nested class is a static member of the outer class.

class BigOuter {
    static class Nested {
    }
}

Example 4.2. Instantiation of the Static Nested Class

The example demonstrates the difference between accessing a static nested class from its enclosing class and non-enclosing class.

class Town {
    static class Street {
        void go() {
            System.out.println("Go to the Street");
        }
    }
}

class City {
    static class District {
        void go() {
            System.out.println("Go to the District");
        }
    }

    public static void main(String[] args) {
        Town.Street street = new Town.Street(); // both class names
        street.go();
        District district = new District(); // access the enclosed class
        district.go();
    }
}
Курс 'Java для начинающих' на Udemy Курс 'Java для начинающих' на Udemy
Comments