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

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

Это предложение от JLS, а оригинальный текст выглядит так:

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

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

4 ответа

Решение

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

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

Оператор 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) вызывается, утверждение отключено (т. е. строка (а) вообще не выполняется). Поскольку включение / отключение assert является частью инициализации класса, следовательно, оно упоминается в указанном вами заявлении.

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

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, Вот что происходит:

  1. Q1.main называется
  2. Q1.main вызывает Baz.testAsserts.
  3. Поскольку Baz расширяет Bar, а Bar не инициализируется, в соответствии с JLS он пытается инициализировать Bar
  4. статический блок бара называется. Помните, что оператор assert включен до того, как его класс завершил инициализацию или вызван assert (что происходит раньше). Который в этом случае true на этом этапе как Bar еще не полностью инициализирован
  5. статика Bar звонки Baz.testAsserts(), Утверждение все еще включено (помните, что отключение утверждения имеет отношение к инициализации класса, и 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,

Вы связались с разделом 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.

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

Я знаю, как читать эту спецификацию, но 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 не должен быть инициализирован в этой точке. Но кроме вышеупомянутой конструкции, я не могу представить случай, когда будет применяться последнее правило, но ни одно из других правил.

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

class TopLevel {
   ...
}

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

assert( condition );

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

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

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

class A {
    static {
        assert ( 2+2 == 4 );
    }
}
Другие вопросы по тегам