Java 内部类(反射,javap -private)JDK 17

huangapple go评论58阅读模式
英文:

Java Inner Classes (reflection, javap -private) JDK 17

问题

I want to know how inner classes mechanism works (JDK 17). I'm reading a book Java and I'm learning about inner classes.

For example I have this:

package com.example;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;

public class TalkingClock {

    private int interval;
    private boolean beep;

    public TalkingClock(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }

    public void start(int interval, boolean beep) {
        class TimePrinter implements ActionListener {

            @Override
            public void actionPerformed(ActionEvent actionEvent) {
                System.out.println("Kiedy uslyszysz dzwiek, bedzie godzina " + Instant.ofEpochMilli(actionEvent.getWhen()));
                if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
                //System.out.println(interval);
            }
        }

        var listener = new TimePrinter();
        var timer = new Timer(interval, listener);
        timer.start();
    }
}

And this info showed after had compiled the code and typed command: javap -private com.example.TalkingClock\$1TimePrinter:

Compiled from "TalkingClock.java"
class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener {
  final com.example.TalkingClock this$0;
  com.example.TalkingClock$1TimePrinter(com.example.TalkingClock);
  public void actionPerformed(java.awt.event.ActionEvent);
}

Okay I can see extra param com.example.TalkingClock in the constructor. but when I will uncomment //System.out.println(interval); in start() method, I can see:

Compiled from "TalkingClock.java"
class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener {
  final int val$interval;
  final com.example.TalkingClock this$0;
  com.example.TalkingClock$1TimePrinter();
  public void actionPerformed(java.awt.event.ActionEvent);
}

Okay extra val$interval is properly added but where is extra param com.example.TalkingClock in the constructor for TalkingClock.this.beep?

On the other hand I have written a reflection code.

In the first case it shows:

class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener{

  com.example.TalkingClock$1TimePrinter(com.example.TalkingClock);

  public void actionPerformed(java.awt.event.ActionEvent);

  final com.example.TalkingClock this$0;
}

in the other:

class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener{

  com.example.TalkingClock$1TimePrinter(com.example.TalkingClock, int);

  public void actionPerformed(java.awt.event.ActionEvent);

  final int val$interval;
  final com.example.TalkingClock this$0;
}

And this info is logically for me. But why the tool javap shows me something else?

Thank you for help!!

//edit

The reflection code:

public static void main(String[] args) throws ClassNotFoundException {

    String name = "com.example.TalkingClock$1TimePrinter";
    Class cl = Class.forName(name);

    String modifiers = Modifier.toString(cl.getModifiers());

    if(modifiers.length() > 0) System.out.print(modifiers + " ");
    if(cl.isSealed()) System.out.print("sealed " + name);
    if(cl.isEnum()) System.out.println("enum " + name);
    if(cl.isRecord()) System.out.println("record " + name);
    if(cl.isInterface()) System.out.println("interface " + name);
    else System.out.print("class " + name);

    Class supercl = cl.getSuperclass();

    if(supercl != null && supercl != Object.class) System.out.print(" extends " + supercl.getName());

    printInterfaces(cl);
    printPermittedSubclasses(cl);
    System.out.println("{\n");
    printConstructors(cl);
    System.out.println();
    printMethods(cl);
    System.out.println();
    printFields(cl);
    System.out.println("}\n");
}

public static void printInterfaces(Class cl) {
    Class<?>[] interfaces = cl.getInterfaces();

    for (int i = 0; i < interfaces.length; i++) {
        if (i == 0) {
            System.out.print(cl.isInterface() ? "extends " : "implements ");
        } else
            System.out.print(", ");
        System.out.print(interfaces[i].getName());
    }
}

public static void printConstructors(Class cl) {
    Constructor[] constructors = cl.getDeclaredConstructors();

    for (Constructor c : constructors) {
        String name = c.getName();
        System.out.print("  ");
        String modifiers = Modifier.toString(c.getModifiers());
        if (modifiers.length() > 0) System.out.print(modifiers + " ");
        System.out.print(name + "(");

        Class[] paramTypes = c.getParameterTypes();

        for (int j = 0; j < paramTypes.length; j++) {
            if (j > 0) System.out.print(", ");
            System.out.print(paramTypes[j].getName());
        }
        System.out.println(");");
    }
}

public static void printMethods(Class cl) {
    Method[] methods = cl.getDeclaredMethods();

    for (Method m : methods) {
        Class retType = m.getReturnType();
        String name = m.getName();
        System.out.print("  ");

        String modifiers = Modifier.toString(m.getModifiers());

        if (modifiers.length() > 0)
            System.out.print(modifiers + " ");
        System.out.print(retType.getName() + " " + name + "(");

        Class[] paramTypes = m.getParameterTypes();

        for (int j = 0; j < paramTypes.length; j++) {
            if (j > 0)
                System.out.print(", ");
            System.out.print(paramTypes[j].getName());
        }
        System.out.println(");");
    }
}

public static void printFields(Class cl) {
    Field[] fields = cl.getDeclaredFields();

    for (Field f : fields) {
        Class type = f.getType();

        String name = f.getName();
        System.out.print("  ");
        String modifiers = Modifier.toString(f.getModifiers());

        if (modifiers.length() > 0) System.out.print(modifiers + " ");
        System.out.println(type.getName() + " " + name + ";");
    }
}

public static void printPermittedSubclasses(Class cl) {
    if (cl.isSealed()) {
        Class<?>[] permittedSubclasses = cl.getPermittedSubclasses();

        for (int i = 0; i < permittedSubclasses.length; i++) {
            if (i == 0)
                System.out.print(" permits ");
            else
                System.out.print(", ");
            System.out.print(permittedSubclasses[i].getName());
        }
    }
}
英文:

I want to know how inner classes mechanism works (JDK 17). I'm reading a book Java and I'm learning about inner classes.

For example I have this:

package com.example;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.time.Instant;
public class TalkingClock {
private int interval;
private  boolean beep;
public TalkingClock(int interval, boolean beep) {
this.interval = interval;
this.beep = beep;
}
public void start(int interval, boolean beep) {
class TimePrinter implements ActionListener {
@Override
public void actionPerformed(ActionEvent actionEvent) {
System.out.println(&quot;Kiedy uslyszysz dzwiek, bedzie godzina &quot; + Instant.ofEpochMilli(actionEvent.getWhen()));
if(TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
//System.out.println(interval);
}
}
var listener = new TimePrinter();
var timer = new Timer(interval, listener);
timer.start();
}
}

And this info showed after had compiled the code and typed command: javap -private com.example.TalkingClock\$1TimePrinter:

Compiled from &quot;TalkingClock.java&quot;
class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener {
final com.example.TalkingClock this$0;
com.example.TalkingClock$1TimePrinter(com.example.TalkingClock);
public void actionPerformed(java.awt.event.ActionEvent);
}

Okay I can see extra param com.example.TalkingClock in the constructor.
but when I will uncomment //System.out.println(interval); in start() method, I can see:

Compiled from &quot;TalkingClock.java&quot;
class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener {
final int val$interval;
final com.example.TalkingClock this$0;
com.example.TalkingClock$1TimePrinter();
public void actionPerformed(java.awt.event.ActionEvent);
}

Okay extra val$interval is properly added but where is extra param com.example.TalkingClock in the constructor for TalkingClock.this.beep?

On the other hand I have written a reflection code.

In the first case it shows:

class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener{
com.example.TalkingClock$1TimePrinter(com.example.TalkingClock);
public void actionPerformed(java.awt.event.ActionEvent);
final com.example.TalkingClock this$0;
}

in the other:

class com.example.TalkingClock$1TimePrinter implements java.awt.event.ActionListener{
com.example.TalkingClock$1TimePrinter(com.example.TalkingClock, int);
public void actionPerformed(java.awt.event.ActionEvent);
final int val$interval;
final com.example.TalkingClock this$0;
}

And this info is logically for me. But why the tool javap shows me something else?

Thank you for help!!

//edit

The reflection code:

public static void main(String[] args) throws ClassNotFoundException {
String name = &quot;com.example.TalkingClock$1TimePrinter&quot;;
Class cl = Class.forName(name);
String modifiers = Modifier.toString(cl.getModifiers());
if(modifiers.length() &gt; 0) System.out.print(modifiers + &quot; &quot;);
if(cl.isSealed()) System.out.print(&quot;sealed &quot; + name);
if(cl.isEnum()) System.out.println(&quot;enum &quot; + name);
if(cl.isRecord()) System.out.println(&quot;record &quot; + name);
if(cl.isInterface()) System.out.println(&quot;interface &quot; + name);
else System.out.print(&quot;class &quot; + name);
Class supercl = cl.getSuperclass();
if(supercl != null &amp;&amp; supercl != Object.class) System.out.print(&quot; extends &quot; + supercl.getName());
printInterfaces(cl);
printPermittedSubclasses(cl);
System.out.println(&quot;{\n&quot;);
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println(&quot;}\n&quot;);
}
public static void printInterfaces(Class cl) {
Class&lt;?&gt;[] interfaces = cl.getInterfaces();
for (int i = 0; i &lt; interfaces.length; i++) {
if (i == 0) {
System.out.print(cl.isInterface() ? &quot; extends &quot; : &quot; implements &quot;);
} else
System.out.print(&quot;, &quot;);
System.out.print(interfaces[i].getName());
}
}
public static void printConstructors(Class cl) {
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors) {
String name = c.getName();
System.out.print(&quot;  &quot;);
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() &gt; 0) System.out.print(modifiers + &quot; &quot;);
System.out.print(name + &quot;(&quot;);
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j &lt; paramTypes.length; j++) {
if (j &gt; 0) System.out.print(&quot;, &quot;);
System.out.print(paramTypes[j].getName());
}
System.out.println(&quot;);&quot;);
}
}
public static void printMethods(Class cl) {
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods) {
Class retType = m.getReturnType();
String name = m.getName();
System.out.print(&quot;  &quot;);
String modifiers = Modifier.toString(m.getModifiers());
if (modifiers.length() &gt; 0)
System.out.print(modifiers + &quot; &quot;);
System.out.print(retType.getName() + &quot; &quot; + name + &quot;(&quot;);
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j &lt; paramTypes.length; j++) {
if (j &gt; 0)
System.out.print(&quot;, &quot;);
System.out.print(paramTypes[j].getName());
}
System.out.println(&quot;);&quot;);
}
}
public static void printFields(Class cl) {
Field[] fields = cl.getDeclaredFields();
for (Field f : fields) {
Class type = f.getType();
String name = f.getName();
System.out.print(&quot;  &quot;);
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() &gt; 0) System.out.print(modifiers + &quot; &quot;);
System.out.println(type.getName() + &quot; &quot; + name + &quot;;&quot;);
}
}
public static void printPermittedSubclasses(Class cl) {
if (cl.isSealed()) {
Class&lt;?&gt;[] permittedSubclasses = cl.getPermittedSubclasses();
for (int i = 0; i &lt; permittedSubclasses.length; i++) {
if (i == 0)
System.out.print(&quot; permits &quot;);
else
System.out.print(&quot;, &quot;);
System.out.print(permittedSubclasses[i].getName());
}
}
}

答案1

得分: 1

"Okay extra val$interval is properly added but where is extra param com.example.TalkingClock in the constructor for TalkingClock.this.beep?"

这个不在那里,因为那没有意义。想象一下,你写了TalkingClock.this.beep.toLowerCase()(beep是一个字符串) - 你会期望有一个字段吗?

你所指的需要在内部类中可用的东西只是TalkingClock.this仅此而已 - 一旦有了这个,代码就可以简单地在此上执行字段查找 beep。是的,它是私有的,但这就是为什么编译器实际上将其设置为包私有的原因(或者,如果你在JDK17上执行此操作,为什么有嵌套成员部分)。访问控制和确保内部类需要的所有内容对它可用大部分是分开的关注点。javac 需要进行一些巧妙的工作,以适应内部类在类文件级别不存在的事实 - 对于这两个概念都是如此。

this$0 就是 TalkingClock.this。因此,在Java代码中的 TalkingClock.this.beep 在类文件级别转换为:

加载 this$0 变量。然后从该变量所指向的对象中获取 beep 字段。

另一方面,我已经写了一个反射代码。

你没有粘贴这段代码,所以这里没有太多可说的。

英文:

> Okay extra val$interval is properly added but where is extra param com.example.TalkingClock in the constructor for TalkingClock.this.beep?

It isn't there because that wouldn't make any sense. Imagine you wrote TalkingClock.this.beep.toLowerCase() (and beep was a string) - would you expect a field for that too?

The thing you are referring to that needs to be available to the inner class is just TalkingClock.this, that is all - once you have that, the code can simply invoke the field lookup for beep on this. Yes, it's private, but that's why the compiler actually made it package private (or, if you're doing this on JDK17, why there's a nestmates section). Access control and making sure all the things the inner class needs are available to it are mostly separate concerns. javac needs to do some fancy footwork to accomodate the fact that at the class file level inner class do not exist - for both of those concepts.

this$0 is TalkingClock.this. Thus, TalkingClock.this.beep in java code turns, at the class file level, into:

load up the this$0 variable. Then fetch the beep field from the object that variable is pointing at.

> On the other hand I have written a reflection code.

You didn't paste this code so there's not much to say here.

huangapple
  • 本文由 发表于 2023年3月4日 00:38:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/75629696.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定