Как внешние классы связаны с внутренними классами в Java? - PullRequest
1 голос
/ 07 ноября 2019

Я знаю, что каждый экземпляр внутреннего класса в java связан с экземпляром его внешнего класса, но мне интересно, как происходит этот процесс.

Более конкретно, когда вы пишете что-то вроде

public class Outer {
    Inner root;

    class Inner {
        public Inner() {
             next = (Math.random() > 0.5)? new Inner(): null;
        }

        Inner next;
    }
}


Как root, root.next и т. Д. ... все связаны с одним и тем же экземпляром Outer? Добавляет ли компилятор параметр во внутренний конструктор?

Ответы [ 2 ]

2 голосов
/ 07 ноября 2019

Как root, root.next и т. Д. Связаны с одним и тем же экземпляром Outer?

Раздел Спецификация языка Java по определению включающих экземпляров во время состояний создания экземпляра класса:

Пусть C будет классом, являющимсясоздание экземпляра, и пусть i будет создаваемым экземпляром . Если C является внутренним классом, то i может иметь непосредственно включающий экземпляр (§8.1.3), определяемый следующим образом:

  • [...]
  • Если C является внутренним классом-членом , то:
    • Если выражение создания экземпляра класса равно unqualified , тогда:
      • Если выражение создания экземпляра класса происходит в статическом контексте, то возникает ошибка времени компиляции.
      • В противном случае, если C является членом класса, охватывающего класс, в которомВыражение создания экземпляра класса появляется , тогда пусть O будет непосредственно включающим класс, членом которого является C. Пусть n будет целым числом таким, чтобы O было n-ным объявлением лексического типа для класса, в котором появляется выражение создания экземпляра класса.

        Непосредственно включающий экземпляр i является n-м лексически включающим экземпляром this.

      • В противном случае возникает ошибка времени компиляции.

Другими словами, когда вы выполняете

next = (Math.random() > 0.5)? new Inner(): null;

Inner это классбыть воплощенным в жизнь;Inner является членом класса, включающего в себя класс (Inner, т. Е. Сам), где появляется new Inner();Outer - это сразу включающий класс Inner;поскольку Inner является внутренним классом Outer, должен быть this, и что this гарантированно будет иметь Outer включающий экземпляр;и, наконец, этот включающий экземпляр становится включающим экземпляром i, при этом создается новый Inner экземпляр.

Короче говоря, он повторно использует тот же экземпляр Outer.

Добавляет ли компилятор параметр в конструктор Inner?

В разделе Спецификация языка Java по формальным параметрам конструкторов говорится:

Конструктор не закрытого внутреннего класса-члена неявно объявляет в качестве первого формального параметра переменную, представляющую непосредственно включающий экземпляр класса (§15.9.2, §15.9.3).

Итак, да.

0 голосов
/ 07 ноября 2019

Во-первых, обратите внимание, что в Java нет поддержки внутренних классов, поэтому фактически внутренний класс скомпилирован как отдельный класс. Это обозначается как Outer$Inner. Вот байт-код:

// class version 52.0 (52)
// access flags 0x20
class Outer$Inner {

  // compiled from: Outer.java
  // access flags 0x0
  INNERCLASS Outer$Inner Outer Inner

  // access flags 0x0
  LOuter$Inner; next

  // access flags 0x1010
  final synthetic LOuter; this$0

  // access flags 0x1
  public <init>(LOuter;)V
   L0
    LINENUMBER 5 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD Outer$Inner.this$0 : LOuter;
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
   L1
    LINENUMBER 6 L1
    ALOAD 0
    INVOKESTATIC java/lang/Math.random ()D
    LDC 0.5
    DCMPL
    IFLE L2
    NEW Outer$Inner
    DUP
    ALOAD 1
    INVOKESPECIAL Outer$Inner.<init> (LOuter;)V
    GOTO L3
   L2
   FRAME FULL [Outer$Inner Outer] [Outer$Inner]
    ACONST_NULL
   L3
   FRAME FULL [Outer$Inner Outer] [Outer$Inner Outer$Inner]
    PUTFIELD Outer$Inner.next : LOuter$Inner;
   L4
    LINENUMBER 7 L4
    RETURN
   L5
    LOCALVARIABLE this LOuter$Inner; L0 L5 0
    LOCALVARIABLE this$0 LOuter; L0 L5 1
    MAXSTACK = 5
    MAXLOCALS = 2
}

Как вы правильно догадались, экземпляр класса Outer передается в конструктор класса Inner здесь public <init>(LOuter;)V

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...