【问题标题】:Implicit constructor in case of anonymous class whose super class is an Inner Class匿名类的超类是内部类的隐式构造函数
【发布时间】:2022-01-14 11:50:01
【问题描述】:

考虑以下来自 JLS 的文章:§15.9.5.1 当匿名类扩展一个内部类时——那么对于匿名类的隐式构造函数——以下是关于隐式构造函数体的规则:

构造函数体由显式构造函数调用(第 8.8.7.1 节)组成 o.super(...) 的形式,其中o构造函数的第一个形参, 而实际参数是构造函数的后续形参, 按照声明的顺序。

以下是我们从中了解到的——:

  1. o - 是类的实例 - 只是封装了匿名类的超类。
  2. 当我们执行o.super(...) 时,实际上是在调用封闭实例的超类。

考虑以下程序:

class A_ {
  public A_(Boolean st){}
  public class B_ {
    public B_(Integer a){}
    public class C_ {
      public C_(String str){}
    }
  }
}

//when creating the anonymous constructor out of C - in a separate class:
public class ThisInAnonymousTesting {
  public static void main(String[] args) {
    A_.B_.C_ member =
        new A_(true)
            .new B_(23)
            .new C_("Hey"){
    };
  }
}

现在当我们反编译匿名类时,我们得到以下信息:

/**
 === Anonymous class Declaration
*/
import A_.B_;
import A_.B_.C_;

final class ThisInAnonymousTesting$1 extends C_ {
// - Rule : the 1st argument is the class instance which is the enclosing instance
// of the Super class(C in this case) - ie B
    ThisInAnonymousTesting$1(B_ x0, String str) {
        x0.getClass();

//But the super() invocation here is for the super class - not for the enclosing instance
        super(x0, str);
    }
}

以下是我的问题:

  1. 为什么我们需要做o.super(...) - 当我们已经将o 的初始化实例传递给匿名类构造函数时?
    • o 仅在其超类已被调用时才会创建。
    • 构造函数中的super() 调用显然试图实例化C_ 类,这很好——因为它是当前匿名类的超类。
  2. 在反编译的版本中,x0.getClass();需要什么-我的意思是为什么JVM需要做getClass()

不确定我对o.super() 子句的解释是否正确?

【问题讨论】:

  • 我无法复制。我没有在字节码中收到任何对getClass 的调用。你用的是什么版本的Java,你用的是什么反编译器?
  • @Sweeper :我的问题主要是关于提到的o.super() 子句-此语法旨在调用封闭实例的super 类-我怀疑为什么需要这样做? - 当封闭实例已经被实例化并通过时? -- 这部分规范是否适用,或者我的理解不正确? -- super() 也可以作为当前类构造函数调用-但不清楚为什么封闭类会发生这种情况?

标签: java constructor inner-classes jls anonymous-inner-class


【解决方案1】:

我想你误解了o.super(...) 的意思。表格声明:

ExpressionName . [TypeArguments] super ( [ArgumentList] ) ; 
Primary . [TypeArguments] super ( [ArgumentList] ) ;

是合格的超类构造函数调用,在 JLS 的 explicit constructor invocations 部分中指定。

它不会调用o 的超类构造函数。它调用封闭类的超类构造函数,将o 作为封闭实例

这是一个简单的例子:

class Outer {
    public static final Outer OUTER1 = new Outer(1);
    public static final Outer OUTER2 = new Outer(2);
    public Outer(int x) {
        this.x = x;
    }

    private final int x;

    class Inner {
        public Inner() {
            System.out.println("In Inner constructor, the enclosing instance's field x is: " + x);
        }
    }

    class InnerSubclass extends Inner {
        public InnerSubclass() {
            OUTER1.super();

            System.out.println("In InnerSubclass constructor, the enclosing instance's field x is: " + x);
        }
    }
}

如果你做Outer.OUTER2.new InnerSubclass();,输出是:

In Inner constructor, the enclosing instance's field x is: 1
In InnerSubclass constructor, the enclosing instance's field x is: 2

OUTER1.super(); 调用Inner 的构造函数(OUTER1 是封闭对象),而不是Outer 的构造函数。请注意,这与仅执行super(); 不同,因为这将使用InnerSubclass 的封闭实例来调用超类构造函数,无论是什么,不一定是OUTER1

所以规范的真正含义是,匿名构造函数将调用超类的构造函数,封闭实例是匿名构造函数的第一个参数。匿名构造函数的第一个参数是什么?这在之前的几行中已经说过:

否则,匿名构造函数的第一个形参表示i相对于S的直接封闭实例的值

在你的情况下,new A_(true).new B_(23)

所以整体效果是这样的:

final class ThisInAnonymousTesting$1 extends C_ {

    ThisInAnonymousTesting$1(B_ o, String str) {
        o.super(str); // recall that this calls C's constructor
    }
}

// the callsite now becomes like this:
A_.B_.C_ member = new ThisInAnonymousTesting$1(new A_(true).new B_(23), "Hey");

注意:ThisInAnonymousTesting$1 extends C_ 不是有效的 Java,但在字节码中是允许的。

在反编译的代码中,您会看到语法super(x0, str);,因为字节码中没有内部类之类的东西。内部类的封闭实例都只是转换为私有字段,并通过构造函数的第一个参数进行分配。因此,如果您查看字节码,o.super(...) 实际上只是 super(o, ...)

考虑:

class Outer {
    class Inner {}
}

Outer$Inner.class 的字节码是:

class Outer$Inner {
  final Outer this$0;

  Outer$Inner(Outer);
    Code:
       0: aload_0          
       1: aload_1          
       2: putfield      #1 // this.this$0 = Outer parameter (aka enclosing instance)
       5: aload_0          
       6: invokespecial #7 // super()
       9: return
}

【讨论】:

  • 为什么我们需要为封闭实例调用超类构造函数 - 我们将传递给隐式匿名类构造函数? - 封闭实例已经是一个完全烘焙的对象 - 我们需要做的就是将它显式传递给匿名类构造函数 - 以便它可以访问封闭类的成员。
  • 另外 - 我试图验证的事实相同 - 我看不到任何 o.super(...) 类型的调用 - 根据上面的论点,理论上它似乎也不正确 - 我们正在传递一个完全烘焙的对象。但规范说:The constructor body consists of an explicit constructor invocation (§8.8.7.1) of the form o.super(...), where o is the first formal parameter of the constructor, and the actual arguments are the subsequent formal parameters of the constructor, in the order they were declared.
  • 直截了当:o.super() 不调用o 的构造函数! o.super()super() 相同,除了我们明确说明封闭实例是什么。你明白为什么要在构造函数的第一行调用超类构造函数吗?
  • 它说:- o : 构造函数的第一个参数 - 即它是匿名类的封闭实例。 o.super() :在匿名构造函数主体中被调用 - 这看起来很奇怪 - 因为 o 应该是完全烘焙
  • @theutonium.18 好吧,如果你觉得语法很奇怪,我帮不了你。是的o 应该在那个时候完全烘焙,o.super() 不应该初始化o。它应该初始化this
猜你喜欢
  • 2015-02-04
  • 2010-09-26
  • 1970-01-01
  • 2014-01-15
  • 1970-01-01
  • 2020-10-31
  • 1970-01-01
  • 2020-04-18
  • 1970-01-01
相关资源
最近更新 更多