preface

This is the third article in our speed-reading series about internal anonymous classes and why they use external variables to add final.

Albert Einstein: “If you cannot explain something simply, you do not really understand it.”

The difference between a=a+b and a+=b

Reload/rewrite, dynamic/static dispatch? (Revised)

Internal anonymous classes use external variables. Why add final

[short essay velocity -4] Whether the new subclass instantiates the parent class

Introduction to Multithreaded programming: Processes, threads, thread safety

primers

A: MDove, I’m A little confused when I’m learning anonymous inner classes. Why do you have such a foreign name? What is an internal anonymous class? Why does it reference external variables with final? Can’t reassign yet?

MDove: Wow, that’s a lot of questions. On the other hand, internally anonymous classes are a really awkward existence. Well, let’s talk about internal anonymous classes today, and solve your questions from the source.

What is an internal anonymous class

MDove: Let’s write a simple demo of a common internal anonymous class:

public class Main {
    public static void main(String[] args) {
        final String name = "Haha";
        new FunLisenter() {
            @Override
            public void fun(a) { System.out.println(name); } }.fun(); }}// External interface
public interface FunLisenter {
    void fun(a);
}
Copy the code

MDove: To answer your first question, what is an internal anonymous class? In the demo above:

new FunLisenter() {
    @Override
    public void fun(a) { System.out.println(name); }}Copy the code

This is the inner anonymous class.

Little A: Huh? Isn’t it just plain new? Why is it an internal anonymous class?

MDove: It’s just plain new? !!!!!!!!! How did you learn Java!! Interface can be new! Tell me out loud, can the interface be new? !

Little A: No! . No… Can… Can you? This is new…

MDove: Interface cannot new! Why is this new out? Because it’s an anonymous inner class, it’s special!

Little A :(whisper beep beep…) What’s special about it?

MDove: The Java language states that interfaces cannot be used by new! Since this is “turtle butt”, new FunLisenter()… It’s definitely not what it looks like. Interface!!!!! I’m not going to bullshit you, but I’m going to go straight to the compiled.class file:

MDove: Keep your eyes wide open and watch carefully! What’s the difference?

Little A: Huh? How can 2 Java files be compiled into 3 class files?

MDove: This is the special existence, we decompile this special Main$1.class file:

final class MainThe $1implements FunLisenter {
    Main$1() {}public void fun(a) {
        System.out.println("Haha"); }}Copy the code

MDove: Is that clear? See?

Little A: Huh? A strange class implements our FunLisenter interface?? Is my new FunLisenter the weird Main$1 class of new?

MDove: Wow, so quick? Think a little deeper. Why anonymous? Is it starting to feel a little bit? From our Java perspective, this class is not visible at all.

Little A: Then why is it called an inner class?

MDove:? Main$1, not inner class what class? Have you ever compiled a class that has an inner class? This is the case with inner class class files.

Little A: Oh, that sounds like it! That is, they are called internally anonymous because, at compile time, the compiler helps us implement our interfaces in the form of internal classes, so that we can use them as new.

MDove: That’s right, you get it right.

Small A: I understand the internal anonymous class, then why add final?

Why does an inner anonymous class reference an external variable with final

MDove: Let’s rewrite a simple piece of code:

public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.fun();
    }
    public void fun(a) {
        // Why is it null, because it avoids the effect of String constants
        final String nameInner = null;
        lisenter = new FunLisenter() {
            @Override
            public void fun(a) { System.out.println(nameInner); } }.fun(); }}Copy the code

MDove: First of all, let’s ask a question about these lines of code: why can an internal anonymous class access nameInner? One method is a stack frame. For local variables, the method ends, the stack frame pops up, and the local variable disappears. So why are internal anonymous classes accessible?

Little A: Yeah, why?

MDove: Are you kidding me? What if I asked you a question?

Little A :(whispering beep beep)… I don’t know.

MDove: Let’s go straight to the decompiled class file:

class MainThe $1implements FunLisenter {
    Main$1(Main var1, String var2) {
        this.this$0 = var1;
        this.val$nameInner = var2;
    }

    public void fun(a) {
        System.out.println(this.val$nameInner); }}Copy the code

MDove: No need to explain? This example not only explains why internal anonymous classes can access local variables, but also shows the problem of holding external references. The local variable nameInner is assigned as a parameter to our internally held external variable when our compile time generates the anonymous inner class. So when we call fun(), we just use this.val$nameInner.

Little A: I see… So why does it have to be final?

MDove: Well, that’s easy to understand. Let me ask you a question first. Is the local variable nameInner the same object as the nameInner of the anonymous inner class in Java code?

Little A: Why do you have to ask? Of course it’s a…

MDove: Yes, from the outside, it is the same. But we also decompiled the bytecode and found that the two are not the same object. Let’s imagine: if we don’t add final. With Java’s design, this is a sure thing: we reassign values in internal anonymous classes, but local variables don’t change at the same time. Because by this design, reassignment is just two variables! So to avoid this, we add final. Change values out of sync? Can’t even modify, what need synchronization!

Design schemes for other languages

Little A: It feels like an awkward design? Is this true of other languages?

MDove: Don’t mention it. This is not true in other languages. For example, C#, a fellow object-oriented language, implicitly wraps variables in a class during compilation. This avoids the problem of out-of-sync changes. Let’s use Java to simulate this scenario:

public class Main {
    public static void main(String[] args) {
        Main main = new Main();
        main.fun();
    }
    public void fun(a) {
        final TempModel tempModel = new TempModel("Haha");
        System.out.println(tempModel.name);
        new FunLisenter() {
            @Override
            public void fun(a) {
                System.out.println(tempModel.name);
                tempModel.name = "Hehe"; } }.fun(); System.out.println(tempModel.name); }}public class TempMain {
    private String name;
    public TempMain(String name) {
        this.name = name; }}Copy the code

MDove: We have a simple object that wraps the variables we want to use. And that’s it, no final effect.

Small A: TempModel plus final?

MDove: Add final because the Java language dictates that, if you think about it, this is an object. Does adding final or not affect the internal values? This is how C# implements local variables.

Small A: It seems to be true, it seems that the bottom design is really A very artistic knowledge.

The end

I am a fresh graduate, recently and friends maintain a public account, the content is that we in the transition from fresh graduate to the development of this way stepped on the pit, as well as our step by step learning records, if interested in friends can pay attention to it, together with fuel ~