The original address

What is an inner class?

If you put the definition of one class inside the definition of another class, that class is an inner class


Why do WE need inner classes?

In general, an inner class inherits from a class or implements an interface, and the code of the inner class operates on the objects of its enclosing class. So you can think of an inner class as providing some kind of window into its outer classes.


The elegance of inner classes:

Each inner class can inherit an implementation independently, regardless of whether the outer class already inherits an implementation.

The inner classes are mainly the following:

  • A: Member inner class
  • B: Local inner class
  • C: Static inner class
  • D: Anonymous inner class

Note:

  1. Once you define a member inner class, you must use the outer class object to create the inner class object, rather than simply new an inner class object.

    That is:Inner class object name = outer class object. new Inner class ();
  2. An external class cannot directly use the member and method drops of an inner class. It can first create an object of the inner class, and then access its member variables and methods through the object of the inner class.
  3. You can first create an object of the inner class and then access its member variables and methods through the object of the inner class. HelloWorld.this.name

A: Member inner class

Returns the classification

Exists as a member of an external class, along with properties and methods of the external class.

public class Outer {
    
    private static int i = 1;
    private int j = 10;
    private int k = 20;


    public static void outerF1(a) {}/** * Static methods of an external class access a member's inner class in the same way that an external class accesses a member's inner class */
    public static void outerF4(a) {
        //step1 create an external class object
        Outer out = new Outer();
        //step2 create an inner class object based on the outer class object
        Inner inner = out.new Inner();
        //step3 access the inner class's methods
        inner.innerF1();
    }

    public static void main(String[] args) {

        /* * outerF4(); The statement of the output and the output of the following three statements as * if you want to create an inner class object directly, can't take it for granted that only with the name of the Outer peripheral class, * can be generated in the shape of a usually inner class object, but also around the class of an object must be used to create them values of an object: * *Outer.Inner outin = out.new Inner() * Therefore, it is not possible to generate an object of the Inner class unless you already have an object of the Outer class. Because the object of this inner class quietly links to the object of the enclosing class that created it. If you are using a static inner class, * you do not need a reference to its enclosing class object. * /
        Outer out = new Outer();
        Outer.Inner outin = out.new Inner();
        outin.innerF1();
    }

    public void outerF2(a) {}/** * Non-static methods of the external class access the member inner class */
    public void outerF3(a) {
        Inner inner = new Inner();
        inner.innerF1();
    }

    /** * In a member inner class, you cannot define a static member ** in a member inner class, you can access all members of the external class */
    class Inner {
        // static int innerI = 100; Static variables are not allowed to be defined in inner classes
        // Instance variables of inner and outer classes can coexist
        int j = 100;
        int innerI = 1;


        void innerF1(a) {
            System.out.println(i);
            // Access the inner class's own variables directly with the variable name
            System.out.println(j);
            // Access the inner class's own variables in the inner class can also use this
            System.out.println(this.j);
            // Use the outer class name to access instance variables in the inner class with the same name as the inner class. The variable name
            System.out.println(Outer.this.j);
            // If the inner class does not have a variable with the same name as the outer class, the outer class variable can be accessed directly with the variable nameSystem.out.println(k); outerF1(); outerF2(); }}}Copy the code

Note: An inner class is a compile-time concept and, once compiled, becomes a completely different class.

For an outer class named Outer and an inner class defined internally named inner. Outer. Class and outer$inner. Class.


# #

B: Local inner class

Returns the classification

Inner classes defined in methods are called local inner classes. Like local variables, a local inner class cannot have access specifiers because it is not part of the enclosing class, but it can access constants within the current code block and all members of the enclosing class.


public class Outer {

    private int s = 100;
    private int outI = 1;

    public static void main(String[] args) {
        // To access a local inner class, you must first have an outer class object
        Outer out = new Outer();
        out.f(3);
    }

    public void f(final int k) {
        final int s = 200;
        int i = 1;
        final int j = 10;


        /** * is defined within the method */
        class Inner {
            // You can define variables with the same name as the external class
            int s = 300;
            int innerI = 100;

            // static int m = 20; You cannot define static variables
            Inner(int k) {
                innerF(k);
            }
            void innerF(int k) {
                // Java If the inner class does not have a variable with the same name as the outer class, the inner class can directly access the instance variable of the outer class
                System.out.println(outI);
                // Local variables (that is, variables within methods) of the external class can be accessed, but the variables must be final
                System.out.println(j);
                //System.out.println(i);
                // If the inner class has a variable with the same name as the outer class, the inner class variable is directly accessed by the variable name
                System.out.println(s);
                // Use this. To access inner class variables
                System.out.println(this.s);
                // Use the outer class name. this. The inner class variable name accesses the outer class variable
                System.out.println(Outer.this.s); }}newInner(k); }}Copy the code

C: Static inner class (nested class) :

Returns the classification

Note: The first two inner classes are similar to variables, so reference variables can be compared

If you don’t need a connection between an inner class object and its enclosing class object, you can declare the inner class static. This is often called a nested class. To understand what static means when applied to an inner class, you must remember that the ordinary 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, you do not need an object of its enclosing class.
  2. A non-static enclosing class object cannot be accessed from an object of a nested class.

Singleton: Because of the loading mechanism of static inner classes, it can be used to handle singleton patterns, and performance objective singleton patterns


public class Outer {
    private static int i = 1;
    private int j = 10;

    public static void outerF1(a) {}public static void main(String[] args) {
        new Outer().outerF3();
    }

    public void outerF2(a) {}public void outerF3(a) {
        // The outer class accesses the static member of the inner class: the inner class. Static members
        System.out.println(Inner.inner_i);
        Inner.innerF1();
        // An external class accesses a non-static member of an inner class: instantiate the inner class
        Inner inner = new Inner();
        inner.innerF2();
    }

    /** * Static inner classes can be public,protected,private */ static inner classes can define static or non-static members */
    static class Inner {
        static int inner_i = 100;
        int innerJ = 200;

        static void innerF1(a) {
            // A static inner class can access only static members of an external class (including static variables and methods)
            System.out.println("Outer.i" + i);
            outerF1();
        }


        void innerF2(a) {
            // A static inner class cannot access non-static members of an external class (including non-static variables and non-static methods)
            // System.out.println("Outer.i"+j);
            // outerF2();}}}Copy the code

The difference between static inner classes and member inner classes

Outer.Inner in = new Outer.Inner(); You don’t need to generate an external class object to do so. This effectively makes a static inner class a top-level class (normally, you can’t put any code inside an interface, but a nested class can be part of the interface because it’s static. Putting nested classes in the namespace of the interface does not violate the rules of the interface.


D: Anonymous Inner Class (from Thinking in Java 3TH)

Returns the classification

An anonymous inner class is an inner class without a name.

When should anonymous inner classes be used?

Anonymous inner classes are appropriate if some of the following conditions are met:

  • Only one instance of the class is used.
  • Class is used immediately after definition.
  • Classes are very small (SUN recommends under 4 lines of code)
  • Naming a class does not make your code easier to understand.

There are a few principles to keep in mind when using anonymous inner classes:

  1. Anonymous inner classes generally cannot have constructors.
  2. Anonymous inner classes cannot define any static members, methods, or classes.
  3. An anonymous inner class can not be public, protected, private, static.
  4. Only one instance of an anonymous inner class can be created.
  5. An anonymous inner class must be used after new to implicitly implement an interface or implement a class.
  6. Because anonymous inner classes are local inner classes, all restrictions on local inner classes apply to them.

The following example seems a little strange:

Return an anonymous inner class in the method
public class Parcel6 {
    public static void main(String[] args) {
        Parcel6 p = new Parcel6();
        Contents c = p.cont();
    }

    public Contents cont(a) {
        return new Contents() {
            private int i = 11;


            public int value(a) {
                returni; }};// A semicolon is needed here}}Copy the code

The cont() method combines the generation of the return value with the definition of the class that represents the return value! Further, this class is anonymous; it has no name. Worse, it looks like you’re about to create a Contents object:

return new Contents()
Copy the code

But, before reaching the semicolon at the end of the statement, you say: “Wait, I want to insert a class definition here” :

return new Contents() {
    private int i = 11;

    public int value(a) {
        returni; }};Copy the code

This weird syntax says: “Create an object that inherits from an anonymous class called Contents.” A reference returned by a new expression is automatically transformed up to a reference to Contents. The syntax for anonymous inner classes is a shorthand form of the following example:

class MyContents implements Contents {
    private int i = 11;

    public int value(a) {
        returni; }}return new MyContents();
Copy the code

In this anonymous inner class, the default constructor is used to generate Contents. The following code shows what to do if your base class needs a constructor with arguments:

public class Parcel7 {
    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Wrapping w = p.wrap(10);
    }

    public Wrapping wrap(int x) {
        // Base constructor call:
        // Pass constructor argument.
        return new Wrapping(x) { 
            public int value(a) {
                return super.value() * 47; }};// Semicolon required}}Copy the code

Simply pass the appropriate arguments to the base class’s constructor, passing x to new Wrapping(x). The semicolon at the end of an anonymous inner class is not used to mark the end of the inner class (as it is in C++). In fact, it marks the end of an expression that happens to contain an inner class. This is therefore consistent with the semicolon used elsewhere.

If you define a member variable in an anonymous class, you can also initialize it:

public class Parcel8 {
    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Destination d = p.dest("Tanzania");
    }

    // Argument must be final to use inside
    // anonymous inner class:
    public Destination dest(final String dest) {
        return new Destination() {
            private String label = dest;

            public String readLabel(a) {
                returnlabel; }}; }}Copy the code

If you have an anonymous inner class that uses an object defined outside of it, the compiler will require its argument references to be final, as in dest(). If you forget, you will get a compile-time error message. If you simply assign a value to a member variable, the method in this example will do. But what if you want to do something like a constructor? It is impossible to have a named constructor in an anonymous class (because it has no name!). , but by instance initialization, you can achieve the effect of “making” a constructor for anonymous inner classes. Do it like this:

abstract class Base {
    public Base(int i) {
        System.out.println("Base constructor, i = " + i);
    }

    public abstract void f(a);
}


public class AnonymousConstructor {
    public static Base getBase(int i) {
        return new Base(i) {
            {
                System.out.println("Inside instance initializer");
            }

            public void f(a) {
                System.out.println("In anonymous f()"); }}; }public static void main(String[] args) {
        Base base = getBase(47); base.f(); }}Copy the code

In this case, the variable I is not required to be final. Because I is passed to the constructor of the base class of the anonymous class, it is not used directly inside the anonymous class. The following example is of the form “parcel” with instance initialization. Note that the dest() arguments must be final, because they are used within anonymous classes.


public class Parcel9 {
    public Destinationdest(final String dest, final float price) {
        return new Destination() {
            private int cost;
            private String label = dest;

            // Instance initialization for each object:
            {
                cost = Math.round(price);
                if (cost > 100)
                    System.out.println("Over budget!");
            }

            public String readLabel(a) {
                returnlabel; }}; }public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.dest("Tanzania".101.395 F); }}Copy the code

In the instance initialization section, you can see a piece of code that should not be executed as part of the initialization of a member variable (the if statement). So for anonymous classes, the actual effect of instance initialization is the constructor. Of course it’s limited: you can’t override instance initializers, so you can only have one constructor.


Access the outside from multiple layers of nested classes

It doesn’t matter how many layers an inner class is nested, it can transparently access all members of the enclosing class it is embedded in, as shown below:


class MNA {
    private void f(a) {}class A {
        private void g(a) {}public class B {
            void h(a) { g(); f(); }}}}public class MultiNestingAccess {
    public static void main(String[] args) {
        MNA mna = newMNA(); MNA.A mnaa = mna.new A(); MNA.A.B mnaab = mnaa.new B(); mnaab.h(); }}Copy the code

You can see that in MNa.a.b, calling methods G () and f() does not require any conditions (even though they are defined as private). This example also shows the basic syntax for creating multiple nested inner class objects from different classes. The “.new “syntax produces the correct scope, so you don’t have to qualify the class name when calling the constructor.


### Inner class overload problem

What happens if you create an inner class, then inherit from its outer class and redefine the inner class? That is, can inner classes be overloaded? This may seem like a useful idea, but “overloading” the inner class as if it were a method of the outer class doesn’t really work:


class Egg {
    private Yolk y;


    public Egg(a) {
        System.out.println("New Egg()");
        y = new Yolk();
    }

    protected class Yolk {
        public Yolk(a) {
            System.out.println("Egg.Yolk()"); }}}public class BigEgg extends Egg {
    public static void main(String[] args) {
        new BigEgg();
    }

    public class Yolk {
        public Yolk(a) {
            System.out.println("BigEgg.Yolk()"); }}}Copy the code

The output is:

New Egg(a)
Egg.Yolk(a)
Copy the code

The default constructor is automatically generated by the compiler, and here is the default constructor for calling the base class. You might think that since you created the BigEgg object, you would be using an “overloaded” technique, but you can see from the output that this is not the case.

This example shows that when you inherit an enclosing class, nothing particularly magical happens to the inner class. The two inner classes are completely separate entities, each in its own namespace. Of course, it is also possible to explicitly inherit an inner class:

class Egg2 {
    private Yolk y = new Yolk();


    public Egg2(a) {
        System.out.println("New Egg2()");
    }

    public void insertYolk(Yolk yy) {
        y = yy;
    }

    public void g(a) {
        y.f();
    }

    protected class Yolk {
        public Yolk(a) {
            System.out.println("Egg2.Yolk()");
        }


        public void f(a) {
            System.out.println("Egg2.Yolk.f()"); }}}public class BigEgg2 extends Egg2 {
    public BigEgg2(a) {
        insertYolk(new Yolk());
    }

    public static void main(String[] args) {
        Egg2 e2 = new BigEgg2();
        e2.g();
    }

    public class Yolk extends Egg2.Yolk {
        public Yolk(a) {
            System.out.println("BigEgg2.Yolk()");
        }


        public void f(a) {
            System.out.println("BigEgg2.Yolk.f()"); }}}Copy the code

The output is:

Egg2.Yolk()
New Egg2(a)
Egg2.Yolk(a)
BigEgg2.Yolk(a)
BigEgg2.Yolk.f(a)
Copy the code

Bigegg2.ke now explicitly inherits this inner class with the extends egg2.ke technique and overrules its methods. Egg2’s insertYolk() method causes BigEgg2 to transform its own ke object upward and pass it to reference Y. So when g() calls y.f(), the overloaded new version of f() is executed. The second call to egg2.ke () is that the constructor for Bigegg2.ke calls the constructor for its base class. You can see that when g() is called, a new version of f() is called.


Internal Class Inheritance (Thinking in Java 3TH P294)

Since the constructor of an inner class uses references to its enclosing class object, things get a little more complicated when you inherit an inner class. The problem is that the reference to the “secret” enclosing class object must be initialized, and there is no default object to join in the inherited class. To solve this problem, use special syntax to explicitly state the relationship between them:


class WithInner {
    class Inner {
        Inner() {
            System.out.println("this is a constructor in WithInner.Inner"); }; }}public class InheritInner extends WithInner.Inner {
    // ! InheritInner() {} // Won't compile
    InheritInner(WithInner wi) {
        wi.super(a); System.out.println("this is a constructor in InheritInner");
    }


    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = newInheritInner(wi); }}Copy the code

The output is:

this is a constructor in WithInner.Inner
this is a constructor in InheritInner
Copy the code

As you can see, InheritInner only inherits from inner classes, not peripheral classes. But when it comes to generating a constructor, the default constructor isn’t good enough, and you can’t just pass a reference to a peripheral class object. In addition, you must use the following syntax inside the constructor:

enclosingClassReference.super();
Copy the code

About Java callback functions

In Java, usually the person who writes another class or library defines an interface, and then you implement that interface, and then you pass an object of that interface as a parameter to someone else’s program, and somebody else’s program will use that interface to call your function and perform subsequent methods if necessary.


public class CallBack {

    public static void main(String[] args) {
        CallBack callBack = new CallBack();
        callBack.toDoSomethings(100.new CallBackInterface() {
            public void execute(a) {
                System.out.println("My request has been processed successfully."); }}); }public void toDoSomethings(int a, CallBackInterface callBackInterface) {
        long start = System.currentTimeMillis();
        if (a > 100) {
            callBackInterface.execute();
        } else {
            System.out.println("A < 100 does not need to execute callback method");
        }
        long end = System.currentTimeMillis();
        System.out.println("This interface callback time:"+ (end - start)); }}public interface CallBackInterface {

    void execute(a);
}
Copy the code

Are Java callbacks implemented based on anonymous inner classes? The answer is yes.

Java callback, can be said to be anonymous inner class wonderful performance, beautiful coding style, is really intoxication ~

Part of this article source code