Inner class details - anonymous inner class

Application scenarios

Since anonymous inner classes are not conducive to code reuse, anonymous inner classes are generally used when it is determined that the inner class will only be used once.

form

public class OutterClass {
    public Runnable task() {
        return new Runnable() {
            @Override
            public void run() {
                System.out.println("Anonymous inner class...");
            }
        };
    }
}
Does this implementation look familiar?
        // Initialize the thread instance
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Anonymous inner class...");
            }
        });

The way we create an instance of a Runnable subclass for a thread is an anonymous inner class. Through this class without a name, we have achieved an elegant format that combines the creation of an instance of the implementation class (hereinafter referred to as a subclass) with the definition of the subclass, which is also called "directly create an instance using the class definition " .

The above code implements the Runnable interface and rewrites the run() method. Of course, we can define a class (non-interface) by ourselves, and then implicitly inherit and rewrite the base class through this anonymous inner class. methods in the class.

Whether inheriting the parent class or implementing an interface, what you actually get is a reference to the parent class or interface. This parent class reference actually points to an instance of the class defined by the anonymous inner class. Therefore, the inherited parent class (or interface) must exist beforehand. Otherwise, the compiler will prompt you to create this class.

rules of use

Several rules for anonymous inner classes obtained through data review and practice :

Rule 1 : The methods in the anonymous inner class are accessed through the parent class reference, so if you define a method that is not in the parent class, then this method cannot be called by the parent class reference. (Can only be used as code sharing between methods in anonymous inner classes).

Rule 2 : An anonymous inner class can either inherit the parent class or implement the interface, but not both. And if you implement an interface, you can only implement one interface.

Rule 3 : An anonymous inner class cannot have a constructor. However, an instance initialization block can be used to achieve the effect of a constructor, but an instance initialization method cannot be overloaded (ie, there is only one such "constructor"). (About instance initialization: "Java Static Initialization, Instance Initialization and Constructor" )

Rule 4: If you want to use an object defined outside of an anonymous inner class, the compiler will require its parameter reference to be final.

Regarding the fourth rule, there is an important and more complex issue involved here.

Use Cases:

/** define the interface */
public interface MyInterface {
    void doSomething();
}
public class TryUsingAnonymousClass {
    // outer class member method
    public MyInterface useMyInterface() {
        final int number = 201855;// final can be omitted after jdk1.8
        final Object obj = new Object();// final can be omitted after jdk1.8
        
        MyInterface myInterface = new MyInterface() {
            // anonymous inner class
            @Override
            public void doSomething() {
                System.out.println("Basic data type used in anonymous inner class: " + number);
                System.out.println("Reference data type used in anonymous inner class: " + obj);
            }
        };
        return myInterface;
    }
    
    public static void main(String[] args) {
        TryUsingAnonymousClass tc = new TryUsingAnonymousClass();
        MyInterface inter = tc.useMyInterface();
        inter.doSomething();
    }
}

output:

Use of primitive data types in anonymous inner classes: 201855
Use reference data types in anonymous inner classes: java.lang.Object@15db9742

We implement the interface MyInterface by way of an anonymous inner class, and use the two local variables defined in the member method useMyInterface() of the outer class:

int number = 201855;
Object obj = new Object();

(After jdk1.8, the effectively final function is added, and developers do not need to explicitly use the final keyword to modify local variables used in local inner classes or anonymous inner classes, which are added by default by the system.)

Therefore, the local variables we use in anonymous inner classes must be constants ( for basic types, their values ​​are constant; for reference types, their references, that is, the addresses they point to are constant ).

If the value is forcibly changed, an error will be reported (this is an attempt when the 1.8 program does not use final to define number, and the system defaults to this value as final):


The life cycle problem of local variables and anonymous inner class instances that have to be introduced

We know that local variables in member methods are defined and initialized at runtime, while local inner classes (including anonymous inner classes) are defined in methods, but they will still be implemented from java files to classes at compile time The conversion of the file, that is, compiled into a class file.

The compile time comes first, and the run time comes after. And we have to use runtime-defined variables at compile time!

How to do? We have two related keywords that can get constants at compile time: static and final   But obviously, static cannot define local variables.

What can final bring to our program?

Read the analysis of the final keyword in "Java Programming Thought" (4th edition, page 140):

A compile-time constant that never changes.

"In-depth understanding of the Java Virtual Machine: JVM Advanced Features and Best Practices" (Second Edition, page 168) also explained the Class file constant pool:

Constant pool (Blogger's Note: This constant pool is a class file constant pool, a non-runtime constant pool, the biggest difference between the two is that the latter is dynamic)
There are two main types of constants: literals and symbolic references. Literals are relatively close to the constant concept of the Java language layer, such as text strings, constant values ​​declared as final, etc.

The anonymous inner class is compiled into a class file, which compiles the final defined local variables into the constant pool of the class file, so we will look at the above code:

public static void main(String[] args) {
        TryUsingAnonymousClass tc = new TryUsingAnonymousClass();
        MyInterface inter = tc.useMyInterface();
        inter.doSomething();
    }

The int type local variables number and Object type obj end their life cycle after the method useMyInterface() is executed, but these two values ​​can still be effectively output by calling the doSomething() method of the inter object below, indicating that these two constants It is not affected by the problem of the end of the life cycle of local variables after the execution of the external class method. In fact, number and obj already exist in the constant pool in the class file corresponding to the anonymous inner class.

Although final-modified constants solve the problem of getting run-time variables at compile time, the side effect of final is that this value cannot be changed.

For the case where the value of the local variable needs to be changed, we can "take over" the value of the local variable by using assignment (scientific name: reference copy = .0) in the anonymous inner class, and then we can change the value at will.

To sum up, it is the recent research and discussion on anonymous inner classes. Combining the usage of the final keyword and the class file constant pool to discuss the final constants of anonymous inner classes from multiple perspectives. If there is any new understanding in the later period, it will continue to be updated. The typos and typesetting discomfort in the text have been corrected and revised by the blogger. If you find any mistakes while reading, please leave a message at the end of the text.


Guess you like

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