Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Nested classes

A class can be declared inside another class. This is done with small helper classes that only their "owner" needs. Nesting shows the relationship between classes and does not clutter the program with extra names.

A static nested class

A class marked static inside another class is an ordinary class; its full name simply includes the name of the outer one: Order.Item. It is not tied to objects of the outer class:

class Order {
    static class Item {
        String title;
        int quantity;
 
        Item(String title, int quantity) {
            this.title = title;
            this.quantity = quantity;
        }
    }
}
 
void main() {
    Order.Item item = new Order.Item("Tea", 2);
    IO.println(item.title + " x" + item.quantity);
}

Output:

Tea x2

An inner class

A nested class without static is called an inner class. Each of its objects is tied to an object of the outer class and sees its fields, even private ones:

class Counter {
    private int count;
 
    class Clicker {
        void click() {
            count++;
        }
    }
 
    Clicker newClicker() {
        return new Clicker();
    }
 
    int getCount() {
        return count;
    }
}
 
void main() {
    Counter counter = new Counter();
    Counter.Clicker clicker = counter.newClicker();
    clicker.click();
    clicker.click();
    IO.println(counter.getCount());
}

Output:

2

The Clicker object increases the counter of exactly the Counter object that created it.

A local class

A class can be declared right inside a method. It is visible only in that method — handy when a helper type is needed in one place. Most often a local record is declared this way:

void main() {
    record Score(String name, int points) {}
 
    Score[] scores = { new Score("Anna", 90), new Score("Oleg", 75) };
    for (Score s : scores) {
        IO.println(s.name() + ": " + s.points());
    }
}

Output:

Anna: 90
Oleg: 75

An anonymous class

An anonymous class is declared and created in a single expression, without a name. It is usually used to implement an interface on the spot:

interface Greeter {
    String greet(String name);
}
 
void main() {
    Greeter polite = new Greeter() {
        @Override
        public String greet(String name) {
            return "Good afternoon, " + name + "!";
        }
    };
    IO.println(polite.greet("Anna"));
}

Output:

Good afternoon, Anna!

If the interface has a single abstract method, a lambda is usually written instead of an anonymous class: Greeter polite = name -> "Good afternoon, " + name + "!";.

Which one to pick

Start with a static nested class: it is simpler and does not hold a reference to an outer object. An inner class is needed only if the class really needs the fields of the outer object. By the way, all classes in this chapter's examples are nested too: the compiler places them inside the implicit class Main.

Common mistake

An object of an inner class cannot exist without an outer object. Trying to create it like a static nested one does not compile:

class Counter {
    private int count;
 
    class Clicker {
        void click() {
            count++;
        }
    }
}
 
void main() {
    Counter.Clicker clicker = new Counter.Clicker();
}

Output:

Main.java:12: error: an enclosing instance that contains Main.Counter.Clicker is required
    Counter.Clicker clicker = new Counter.Clicker();
                              ^

Create the object through an outer one — counter.new Clicker() — or make the class static if it does not need the outer object's fields.

Thanks for reading!

Donate to the site — we work hard for you.