By: no dishes studio – Marklux

Source: Marklux’s Pub

The copyright belongs to the author, please indicate the source

Java inner class

The basic definition

This is called a member inner class:

public class OuterClass {
    private String name;
    private int age;
    class InnerClass {
        public InnerClass(){
            name = "mark";
            age = 20;
        }
        public void echo() {
            System.out.println(name + ""+ age); }}}Copy the code

thinking

The simple example above also contains many questions to consider:

  • How are inner classes instantiated?
  • Can the inner class change the properties of the outer class, and what is the relationship between the two?
  • What is the point of inner classes?

To answer these three questions, it is important to make it clear that the inner class is dependent on the outer class. In other words, the inner class has references to the outer class. With that in mind, the above question is easy to answer.

Instantiation and data access

A connection is formed between the inner class and the enclosing class, giving the inner class unlimited access to any property in the enclosing class. As in the example above, the private properties in the OuterClass can be accessed arbitrarily from within the InnerClass.

Similarly, the inner class is dependent on the existence of the enclosing class, so we cannot instantiate the inner class directly. Instead, we must instantiate the enclosing class before we can instantiate the inner class (note that we can instantiate the inner class directly in the member method of the enclosing class) :

public static void main(String[] args) {
        InnerClass inner = new OuterClass().new InnerClass();
        inner.echo();
}
Copy the code

Create the outer class using.new of the outer class.

We also know that the connection between the inner class and the outer class is achieved by the inner class holding a reference to the outer class. To obtain this reference, use the.this of the outer class, as shown in the following test case

public class OuterClass {
    private String name;
    private int age;
    class InnerClass {
        public InnerClass(){
            name = "mark";
            age = 20;
        }
        public void echo() {
            System.out.println(name + "" + age);
        }
        public OuterClass getOuter() {
            return OuterClass.this;
        }
    }

    @Test
    public void test() { OuterClass outer = new OuterClass(); InnerClass inner = outer.new InnerClass(); Assert.assertEquals(outer, inner.getOuter()); }}Copy the code

The role of inner classes

Inner classes are cumbersome to create and confusing to use, so what is the point of inner classes?

Implementing multiple inheritance

This is probably the most important reason why inner classes exist, as explained in Thinking in Java:

The most interesting reason for using inner classes is that each inner class can inherit an implementation independently, so it doesn’t matter whether the enclosing class already inherits an implementation.

As we all know, Java does away with multiple inheritance of classes in C++ (but allows multiple implementations of interfaces), but in practical programming, it is inevitable that the same class needs to inherit from two classes at the same time, so it can be implemented using inner classes.

Let’s say we have two abstract classes

public abstract class AbstractFather {
    protected int number;
    protected String fatherName;

    public abstract String sayHello();
}

public abstract class AbstractMother {
    protected int number;
    protected String motherName;

    public abstract String sayHello();
}
Copy the code

If you want to inherit these two classes at the same time, is bound to cause the conflict of number variables, as well as the sayHello method of conflict, these problems in c + + is a complicated scheme to realize, if you use the inner class, you can use two different class to inherit different base class, and can according to their own needs to organize the data access:

public class TestClass extends AbstractFather {
    @Override
    public String sayHello() {
        return fatherName;
    }

    class TestInnerClass extends AbstractMother {
        @Override
        public String sayHello() {
            returnmotherName; }}}Copy the code

other

(From Think in Java)

  1. An inner class can have multiple instances, each of which has its own state information and is independent of the information of other peripheral objects.

  2. Within a single enclosing class, you can have multiple inner classes implement the same interface in different ways, or inherit from the same class.

  3. The time at which the inner class object is created does not depend on the creation of the outer class object.

  4. The inner class does not have a confusing “IS-A” relationship; it is a separate entity.

  5. The inner class provides better encapsulation and is not accessible by any other class except the enclosing class.

Classification of inner classes

The inner classes created in the above example are all member inner classes. There are actually three other inner classes in Java:

  1. Local inner class

    Nested in a method or scope, you generally don’t want this class to be publicly available. A local inner class has a narrower scope than a member inner class, and cannot be accessed outside the method or scope. It is generally used to implement some private auxiliary functions internally.

    Defined in a method:

    public class Parcel5 {
    public Destionation destionation(String str){
        class PDestionation implements Destionation{
            private String label;
            private PDestionation(String whereTo){
                label = whereTo;
            }
            public String readLabel() {returnlabel; }}return new PDestionation(str);
    }
    
    public static void main(String[] args) {
        Parcel5 parcel5 = new Parcel5();
        Destionation d = parcel5.destionation("chenssy"); }}Copy the code

    Define in scope:

    public class Parcel6 {
    private void internalTracking(boolean b){
        if(b){
            class TrackingSlip{
                private String id;
                TrackingSlip(String s) {
                    id = s;
                }
                String getSlip() {return id;
                }
            }
            TrackingSlip ts = new TrackingSlip("chenssy");
            String string = ts.getSlip();
        }
    }
    
    public void track(){
        internalTracking(true);
    }
    
    public static void main(String[] args) {
        Parcel6 parcel6 = new Parcel6();
        parcel6.track();
    }
    Copy the code

} ` ` `

  1. Static inner class

    A static inner class is a static inner class. The main difference between a static inner class and a normal member inner class is that the static inner class has no reference to the enclosing class. Therefore, its creation does not depend on the enclosing class, but it cannot use any non-static member variables and methods of the enclosing class.

    A good use of static inner classes is to create thread-safe singletons:

    public class Singleton {  
        private static class SingletonHolder {  
            private static final Singleton INSTANCE = new Singleton();  
        }  
        private Singleton (){}  
        public static final Singleton getInstance() {  
            returnSingletonHolder.INSTANCE; }}Copy the code

    This takes advantage of a feature of the JVM: static inner classes are implemented at class load time, so they are not subject to multithreading, which naturally prevents multiple instances.

  2. Anonymous inner class

    An anonymous inner class is an unnamed inner class that is often used when we need to quickly create multiple threads:

    new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello");
            }
        }).start();
    Copy the code

    We can now use the λ expression as well. Let’s forget about the relationship between the two. Let’s take a look at some things to note when using anonymous inner classes:

    • Anonymous inner classes have no access modifiers and no constructors
    • An anonymous inner class exists attached to an interface and cannot be created if the interface it inherits does not exist
    • If an anonymous inner class is to access a local variable, the data must be final

    Those familiar with functional programming will notice that anonymous inner classes are somewhat similar to function closures (which is why they can be replaced by the lambda expression), but there are actually some differences. We compare closures and inner classes in the following sections.

Java inner classes and closures

What is a closure?

As discussed in the previous article on closures, closures can be described in one sentence:

A closure is an entity composed of a function and its associated reference environment (i.e. : closure = function + reference environment)

Still write a very simple closure in Go for later comparison with Java anonymous inner classes:

func Add(y int) {
	return func(x int) int {
		return x + y
	}
}

a := Add(10)
a(5) // return 15
Copy the code

Now that we know that the returned closure contains the variable Y provided in the Add function (the context in which the closure was created), let’s think about how the closure is actually implemented.

By definition, the closure = function + reference environment. In this case, the reference environment is the local variable Y. Can we use a structure to define the closure?

type Closure struct {
	F func(x int) int
	y *int
}
Copy the code

That is, the closure contains the function itself and a reference to the local variable y.

In particular, if y is a variable defined in the stack of Add, then when Add() is called, y is destroyed. Using the original pointer to access Y will cause problems, so here’s a rule:

External variables referenced in closures must be allocated on the heap

In fact, the Go compiler uses escape Analyze to identify the scope of variable Y when dealing with the closure, and when it finds that variable Y is referenced by a closure, it moves it to the heap (a process known as variable escape).

To summarize, closure is understood from the bottom, is the function itself and its required external variables reference, R big words to describe the closure creation process:

capture by reference

Inner classes and closures

At the end of our definition of closures, does “references to required external variables” look a lot like references to enclosing classes by inner classes in Java?

So everyone assumes that closures don’t exist in Java. In fact, Java is so full of closures (objects are closures) that we don’t even know we’re using closures. The member inner class is a prime example because it holds a reference to an enclosing class.

public class OuterClass {
    private int y = 10;
    private class Inner {
        public int innerAdd(int x) {
            return x + y;
        }
    }

    public static void main(String[] args) {
        OuterClass outer = new OuterClass();
        Inner inner = outer.new Inner();
        System.out.println(inner.innerAdd(5)); //result: 15
    }
}
Copy the code

This usage is exactly the same as the closure demonstrated earlier in Go, although it is much simpler to write in Go (after all, functional programming is naturally supported).

Are anonymous inner classes closures?

When WE talked about anonymous inner classes, we talked about closures. Are anonymous inner classes closures?

Let’s start by trying to create a closure like structure with anonymous inner classes:

interface AnnoInner {
    int innerAdd(int x);
}

public class OuterClass {
    private int y = 100;
    public AnnoInner getAnnoInner() {
        int z = 10;
        return new AnnoInner() { @Override public int innerAdd(int x) { // z = 20; An errorreturnx + y + z; }}; } public static void main(String[] args) { OuterClass outer = new OuterClass(); AnnoInner inner = outer.getAnnoInner(); System.out.println(inner.innerAdd(5)); //result: 115 } }Copy the code

As you can see, the anonymous inner class can refer to the enclosing member variable Y as well, but for the local variable Z, if you try to change its value in the anonymous inner class (no problem reading it), the compiler will report an error.

This also corresponds to the rule introduced above when introducing anonymous inner classes:

If an anonymous inner class is to access a local variable, that variable must be final

The result is that Java implements anonymous inner class access to external local variables rather than lifting local variables to the heap and passing references, as the Go compiler does. Instead, a copy of the value of the local variable is created for use by the anonymous inner class.

So the above example in the Java compiler implementation can be easily understood as follows:

return new AnnoInner() { @Override public int innerAdd(int x) { int copyZ = z; // create a copy of zreturnx + y + copyZ; }};Copy the code

So, an anonymous inner class in Java is an incomplete closure, which in R big terms is:

Capture by value instead of capture by reference

Reference reading:

  • Does the JVM specification allow you to create closures?
  • Why can’t A Java closure pass out a value in a way other than a return value?