Thinking in Java - Study Notes - (10) Inner Class

Java Programming Ideas - Chapter 10 - Inner Classes

The definition of one class can be placed inside another class, this is the inner class.

Inner classes are a very useful feature because they allow you to group logically related classes together and control the visibility of classes located inside. However, inner classes and compositions are completely different concepts.

link to external class

When an object of an inner class is created , there is a connection to the enclosing object that created it, so it can access all members of its enclosing object without any special conditions. In addition, the inner class also has access to all elements of its enclosing class.

When an object of an outer class creates an inner class object, the inner class object must secretly capture a reference to that outer class object. Then, when accessing a member of this enclosing class, that reference is used to select the member of the enclosing class. (Inner class is not static )

Using .this and .new

It is not possible to create an inner class object before owning the outer class object. This is because the inner class object is implicitly connected to the outer class object that created it. But if you create a nested class (static inner class) then it doesn't need a reference to the outer class object.

// .new
public class DotNew {

    public class Inner {
    }

    public static void main(String[] args) {
        DotNew dn = new DotNew();
        DotNew.Inner dni = dn.new Inner();
    }
}
// .this
public class DotThis {
    void f() {
        System.out.println("DotThis.f()");
    }

    public class Inner {
        public DotThis outer() {
            return DotThis.this;
            // A plain "this" would be Inner's "this"
        }
    }

    public Inner inner() {
        return new Inner();
    }

    public static void main(String[] args) {
        DotThis dt = new DotThis();
        DotThis.Inner dti = dt.inner();
        dti.outer().f();
    }
}

Inner classes and upcasting

When transforming the body into its base class, especially into an interface, inner classes come into play. (Getting a reference to an interface from an object that implements an interface is essentially the same as upcasting to the object's base class.) This is because this inner class—the implementation of an interface— Ability to be completely invisible and unusable. All you get is a reference to the base class or interface, so you can easily hide implementation details.


interface Destination {
    String readLabel();
}

interface Contents {
    int value();
}

class Parcel4 {
    private class PContents implements Contents {
        private int i = 11;

        @Override
        public int value() {
            return i;
        }
    }

    protected class PDestination implements Destination {
        private String label;

        private PDestination(String whereTo) {
            label = whereTo;
        }

        @Override
        public String readLabel() {
            return label;
        }
    }

    public Destination destination(String s) {
        return new PDestination(s);
    }

    public Contents contents() {
        return new PContents();
    }
}

public class TestParcel {
    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Contents c = p.contents();
        Destination d = p.destination("Tasmania");
        // Illegal -- can't access private class
        //! Parcel4.PContents pc = p.new PContents();
    }
}

Inner classes in method and scope

Inner classes can be created in any scope (the creation of a class is unconditional, it is actually compiled with other classes); it is not available outside the scope, otherwise it is the same as a normal class.

anonymous inner class

class Wrapping {
    private int i;

    public Wrapping(int x) {
        i = x;
    }

    public int value() {
        return i;
    }
}

public class TestParcel {

    public Wrapping wrapping(int x) {
        return new Wrapping(x) {
            public int value() {
                return super.value() * 47;
            }
        };
    }

    public static void main(String[] args) {
        TestParcel p = new TestParcel();
        Wrapping w = p.wrapping(10);
        System.out.printf("w.value(): %d\n",w.value());
    }
}

Factory method revisited

interface Service {
    void method1();

    void method2();
}

interface ServiceFactory {
    Service getService();
}

class Implementation1 implements Service {

    @Override
    public void method1() {
        System.out.println(getClass().getSimpleName() + ": method1");
    }

    @Override
    public void method2() {
        System.out.println(getClass().getSimpleName() + ": method2");
    }
    // jdk1.8
    public static ServiceFactory factory = Implementation1::new;
}


class Implementation2 implements Service {

    @Override
    public void method1() {
        System.out.println(getClass().getSimpleName() + ": method1");
    }

    @Override
    public void method2() {
        System.out.println(getClass().getSimpleName() + ": method2");
    }
    // jdk1.8
    public static ServiceFactory factory = Implementation2::new;
}

public class Factories {
    public static void serviceConsumer(ServiceFactory fact) {
        Service s = fact.getService();
        s.method1();
        s.method2();
    }

    public static void main(String[] args) {
        serviceConsumer(Implementation1.factory);
        serviceConsumer(Implementation2.factory);
    }
}

nested class

If there is no need for a connection between the inner class object and its enclosing class object, then the inner class can be declared static . This is often called a nested class . To understand what static means when applied to inner classes, it is important to remember that a normal inner class object implicitly holds a reference to the enclosing class object that created it. However , this is not the case when the inner class is static .

Nested classes mean:

  1. To create an object of a nested class, an object of its enclosing class is not required.

  2. A non-static enclosing class object cannot be accessed from an object of a nested class.

There is one more difference between nested classes and ordinary inner classes. The fields and methods of ordinary inner classes can only be placed at the outer level of the class, so ordinary inner classes cannot have static data and static fields, nor can they contain nested classes. But nested classes can contain all of these things.

class inside interface

Normally, you cannot put any code inside an interface, but nested classes can be part of an interface. Any class placed on an interface is automatically public and static . Because the class is static , it just puts the nested class in the namespace of the interface, which does not violate the rules of the interface. It is even possible to implement its outer interface in an inner class, if you want to create some common code so that it can be shared by all the different implementations of an interface, it is convenient to use a nested class inside an interface.

Access members of outer class from multiple levels of nested classes

It doesn't matter how many levels an inner class is nested - it has transparent access to all members of all the enclosing classes in which it is embedded.

You can see that in MAB, calling the methods g() and f() does not require any conditions (even though they are defined as private ). The ".new" syntax produces the correct scope, so it is not necessary to qualify the class name when calling the constructor.

class M {
    private void f() {}
    class A {
        private void g() {}
        public class B {
            void h() {
                g();
                f();
            }
        }
    }
}

public class MultiNestingAccess {
    public static void main(String[] args) {
        M m = new M();
        M.A ma = m.new A();
        M.A.B mab = ma.new B();
        mab.h();
    }
}

Why do you need inner classes

In general, an inner class inherits from a class or implements an interface, and the code operations of the inner class create objects of its surrounding classes. So it can be considered that the inner class provides some kind of window into its enclosing class.

One of the questions the inner class has to answer is: if you just need a reference to an interface, why not implement that interface through the enclosing class?

The answer is: "If this satisfies the requirement, then it should." So what is the difference between an inner class implementing an interface and an outer class implementing that interface? The answer is: the latter does not always enjoy the convenience brought by the interface, and sometimes even requires the implementation of the interface. So the most attractive reasons to use inner classes are:

Each inner class can inherit one (interface) implementation independently, so whether or not the outer class has inherited a certain (interface) implementation has no effect on the inner class.

Closures and callbacks

A closure is a callable object that records information from the scope in which it was created. Through this definition, it can be seen that the inner class is an object-oriented closure, because it not only contains the information of the object of the outer class (the scope in which the inner class is created), but also automatically has a reference to the object of the outer class, which acts here. Within the domain, inner classes have the right to operate on all members, including private members.

Inner classes and control framework

An application framework is a class or set of classes designed to solve a particular problem.
Control frameworks are a special class of application frameworks that address the need to respond to events.

public class Controller {
    // A class from java.util to hold Event objects:
    private List<Event> eventList = new ArrayList<Event>();
    public void addEvent(Event c) { eventList.add(c); }
    public void run() {
    while(eventList.size() > 0) {
        // Make a copy so you're not modifying the list
        // while you're selecting the elements in it
        for(Event e : new ArrayList<Event>(eventList))
            if(e.ready()) {
                System.out.println(e);
                e.action();
                eventList.remove(e);
            }
    }
}

The run() method loops through the eventList, looking for a ready (ready()) Event object to run. For each ready event found, use the object's toString() to print its information, call its action() method, and then remove the event from the queue.

Note that in the current design you don't know exactly what Event does . That's the crux of this design, "to separate things that change from things that don't change".

This is exactly what inner classes do, inner classes allow:

1) The complete implementation of the control frame is created by a single class, so that the implementation details are encapsulated. The inner class is used to represent the various action() s necessary to solve the problem .

2) The inner class can easily access arbitrary members of the outer class, so this implementation can be avoided to become clumsy.

Inheritance of inner classes

class WithInner {
    class Inner {}
}

public class InheritInner extends WithInner.Inner {
    //! InheritInner() {} // Won't compile
    InheritInner(WithInner wi) {
        wi.super();
    }
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
    }
}

Can inner classes be inherited?

When an outer class is inherited, nothing magically happens to the inner class. These two inner classes are completely separate entities, each in its own namespace. It is OK to explicitly inherit from an inner class.

local inner class

Reasons to use local inner classes:

  1. A named initializer is required, or an overloaded initializer is required, and anonymous inner classes can only be used for instance initialization.

  2. More than one object of this inner class is required.

inner class identifier

Inner classes must also generate a .class file to contain their Class object information. There are strict rules for naming these class files: the name of the enclosing class, followed by " $ ", followed by the name of the inner class.

If the inner class is anonymous, the compiler will simply generate a number as its identifier.

Summarize

The use of features such as interfaces, inner classes, and polymorphism should be considered in the design phase.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325152115&siteId=291194637