Что означает "T - класс верхнего уровня, и выполняется оператор утверждения, лексически вложенный в T". имею в виду? - программирование

Что означает "T - класс верхнего уровня, и выполняется оператор утверждения, лексически вложенный в T". имею в виду?

Я изучаю "Инициализацию классов и интерфейсов", и он говорит: "T - класс верхнего уровня, и выполняется инструкция утверждения, лексически вложенная в T". Может ли кто-нибудь сказать мне, что означает "T - класс верхнего уровня, и выполняется оператор утверждения, лексически вложенный в T". на примере?

Это предложение от JLS, а исходный текст выглядит следующим образом:

Класс или тип интерфейса T будет инициализирован непосредственно перед первым вхождением любого из следующих значений:

  • T - это класс и создается экземпляр T.
  • T - класс, и статический метод, объявленный T, вызывается.
  • Назначено статическое поле, объявленное T.
  • Используется статическое поле, объявленное T, и поле не является постоянной переменной (§4.12.4).
  • T - класс верхнего уровня, и выполняется инструкция assert (§14.10), лексически вложенная в T.
4b9b3361

Ответ 1

Я могу дать ему частичное объяснение. Это относится к включению/отключению утверждения. Утверждение разрешено аргументом -ea vm.

Важным моментом в assert является:

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

Предположим, что -ea не указан, и вы запускаете приведенный ниже код:

 public class Q1 {
    public static void main(String[] args) {
        Bar b = new Bar();
    }
}
class Bar {
    static {
        boolean enabled = false;
        assert  enabled = false; //line(a)
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
        System.out.println("as");
        Baz.testAsserts();
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

В приведенном выше примере, когда инициализируется b, Java гарантирует, что до вызова line(a) утверждение отключается (т.е. строка (a) вообще не выполняется). Поскольку assert enable/disable является частью инициализации класса, поэтому он упоминается в вашем показанном заявлении.

Причина, почему упоминается класс верхнего уровня, а не любой другой класс - это. Более подробное поведение здесь:

public class Q1 {
    public static void main(String[] args) {
        Baz.testAsserts(); 
        // Will execute after Baz is initialized.
    }
}
class Bar {
    static {
        Baz.testAsserts();
        // Will execute before Baz is initialized!
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

Даже флаг -ea не используется, но он выбрасывает AssertionException. Вот что происходит:

  • Q1.main называется
  • Q1.main вызывает Baz.testAsserts.
  • Поскольку Baz расширяет Bar и Bar, не инициализируется, поскольку JLS пытается инициализировать Bar
  • статический блок Bar вызывается. Помните, что утверждение assert включено до того, как его класс завершил инициализацию или вызывается assert (что когда-либо случается в первую очередь). Что в этом случае true на этом этапе, поскольку Bar все еще не полностью инициализировано
  • static <& t210 > вызывает Baz.testAsserts(). Утверждение все еще включено (помните, что отключение assertion связано с инициализацией класса, и Bar все еще не полностью инициализирован). Теперь Baz.testAsserts() выбрасывает AssertionException.

Выше - отверстие петли. JLS только гарантирует, что перед выполнением любого assert в классе верхнего уровня он отключит/активирует (как и любой аргумент vm). Но если это не класс верхнего уровня, то поведение зависит от инициализации класса верхнего уровня. Чтобы это объяснить, см. Это:

class Bar {
    static {
        //Baz.testAsserts();
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
        // Will execute before Baz is initialized!
    }
}
class Baz extends Bar {
    static void testAsserts() {
        boolean enabled = false;
        assert  enabled = false;
        System.out.println("Asserts " + 
               (enabled ? "enabled" : "disabled"));
    }
}

Это печатает Asserts disabled Asserts disabled, поскольку Bar хорошо инициализирован. Bar инициализация отключает assert для класса и, следовательно, для Baz.

Ответ 2

Я знаю, как я буду читать эту спецификацию, но OpenJDK 1.7.0_40 не ведет себя так, как указано, и Oracle JDK 1.7.0_25.

Класс верхнего уровня - это класс, не вложенный внутри любого другого. Оператор утверждения может выполняться в исполняемом коде, то есть в блоке метода, конструктора или статического инициализатора. Большинство из этих случаев обрабатываются другими элементами: статические методы уже рассмотрены, другие методы, а также конструкторы относятся к созданию объекта указанного класса, а статический блок инициализатора является частью процесса инициализации, который является результатом любого других событий.

Таким образом, единственный способ, которым я могу думать о том, чтобы иметь лексически вложенный оператор без запуска любого из этих случаев, был бы через вложенный класс. Например. что-то вроде этого:

class Outer {
    static {
        System.out.println("Outer initialized");
    }
    static class Nested {
        static void foo() {
            assert System.out == null;
        }
    }
}

Но если я запустил Outer.Nested.foo() с включенными утверждениями, то я получу ошибку утверждения (поэтому оператор был выполнен), но не сообщение Outer initialized. Таким образом, класс верхнего уровня не был инициализирован, хотя выполнялся лексически вложенный оператор assert.

Либо я неправильно понимаю спецификацию здесь, либо упомянутые реализации не следуют за ней.

Что касается обоснования: я считаю, что основным требованием этого требования является то, что включение и отключение утверждений осуществляется через скрытое статическое (и iirc. final) поле класса. Поэтому, когда оператор assert выполняется, он должен проверить это поле, следовательно, это поле должно быть инициализировано, поэтому класс должен быть инициализирован. Но в приведенном выше коде соответствующее поле, скорее всего, имеет значение Outer.Nested, а не самого Outer. Поэтому имеет смысл, что Outer не нужно инициализировать в этой точке. Но помимо вышеприведенной конструкции я не могу придумать случай, когда последнее правило будет применяться, но ни одно из других правил.

Ответ 3

Это класс верхнего уровня:

class TopLevel {
   ...
}

Это утверждение:

assert( condition );

где condition - некоторое булевское выражение.

A лексически вложен в B, если он встречается внутри фигурных скобок определения B. Например, поля, методы, статические блоки лексически вложены в определение класса. Заявления лексически вложены в методы или статические блоки. Локальные определения вложены в методы или блоки, которые сами вложены в методы.

Следовательно, утверждение assert, лексически вложенное в класс верхнего уровня, может быть:

class A {
    static {
        assert ( 2+2 == 4 );
    }
}

Ответ 4

Вы связались с в разделе 12.4.1 JLS для Java 7.

В java-ошибке JDK-8043189 "12.4.1: Должен ли инициировать инициализацию внешних классов?" есть комментарий, поясняющий, что последней буквой списка, которая гласит: "T - это класс верхнего уровня, и выполняется инструкция assert (§14.10), лексически вложенная в T," просто неверна, и она была ошибочно вставлена ​​в JLS в версии 6, и он оставался там до версии 7 просто потому, что никто этого не заметил.

Я обнаружил, что оператор все еще присутствует в JLS для Java 8, но он, наконец, был удален из параграф 12.4.1 JLS для Java 9.

Итак, нижняя строка - не пытайтесь сделать из нее какой-то смысл, потому что нечего делать; это не истинное утверждение, и оно никогда не было.