14

I am learning the " Initialization of Classes and Interfaces", and it says "T is a top-level class, and an assert statement lexically nested within T is executed." Could any one tell me what does "T is a top-level class, and an assert statement lexically nested within T is executed." mean by a example?

This sentence is from JLS, and original text is like this :

A class or interface type T will be initialized immediately before the first occurrence of any one of the following:

  • T is a class and an instance of T is created.
  • T is a class and a static method declared by T is invoked.
  • A static field declared by T is assigned.
  • A static field declared by T is used and the field is not a constant variable (§4.12.4).
  • T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed.
Community
  • 1
  • 1
huashui
  • 1,758
  • 2
  • 16
  • 24
  • 2
    Can you give some context to your question? – Hovercraft Full Of Eels Jul 23 '13 at 03:06
  • I believe he is talking about the Java Language Specification, section 12.4.1. http://docs.oracle.com/javase/specs/jls/se5.0/html/execution.html#12.4.1 – Mark M Jul 23 '13 at 03:11
  • Maybe you are missing to copy-paste a part of the sentence, what I understand is that the assert statement (introduced a while ago in Java 1.4) inside a top-level class (a top-level class is a class not nested inside another class) will get executed if something else (that you don't specify) happens – morgano Jul 23 '13 at 03:13
  • I am sorry that I missed some sentences. I have added it. – huashui Jul 23 '13 at 03:27
  • 2
    You haven't accepted any answers in your previous questions, please accept them else it gets discouraging to answer. – Jatin Jul 23 '13 at 08:11
  • Note: all answers trying to explain what this message means are wrong, because the message itself is wrong. It has been removed from the Java Language Specification. Just skip directly to my answer: https://stackoverflow.com/a/47999746/773113 – Mike Nakis Feb 01 '21 at 14:18

4 Answers4

5

I can give a partial explanation to it. It refers to enabling/disabling assertion. Assertion is enabled by -ea vm argument.

An important point about assert is:

An assert statement that is executed before its class has completed initialization is enabled.

Suppose -ea is not given and you run the below code:

 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"));
    }
}

In the above example when b is initialized, Java guarantees that before line(a) is called, the assertion is disabled (i.e. line(a) is not executed at all). Because assert enable/disable is a part of class initalization, hence it is mentioned in your shown statement in question.

The reason, why top-level class is mentioned and not every other class is this. More detailed behavior here:

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"));
    }
}

Even -ea flag is not used, still it throws an AssertionException. Here is what happens:

  1. Q1.main is called
  2. Q1.main calls Baz.testAsserts.
  3. Because Baz extends Bar and Bar is not initialized, as per JLS it tries to initialize Bar
  4. static block of Bar is called. Remember assert statement is enabled before its class has completed initialization or assert is called (which ever happens first). Which in this case is true at this stage as Bar is still not completely initialized
  5. static of Bar calls Baz.testAsserts(). The assert is still enabled (remember disabling assertion has got to do with class initialization and Bar is still not completely initialized). Now Baz.testAsserts() throws AssertionException.

Above is a loop hole. JLS only guarantees that before executing any assert in Top level class, it will disable/enable (as whatever vm argument is given) it. But if it is not a top level class, then the behavior depends on the initialization of top-level class. To explain this, see this:

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"));
    }
}

This prints Asserts disabled Asserts disabled as Bar is well initialized. Bar initialization disables assert for the class and hence for Baz.

Jatin
  • 31,116
  • 15
  • 98
  • 163
  • I don't think `assert enable = false;` is a valid statement. – Ingo Jul 23 '13 at 08:19
  • It is as per http://docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.10. It assigns `false` to `enabled` and returns enabled. – Jatin Jul 23 '13 at 08:22
  • Or if it is confusing, replace it with someother assert statement. – Jatin Jul 23 '13 at 08:23
  • Yes, you're right, this is an assignement expression then. Still I'd prefer `assert (enabled = false)` for stylistic reasons. – Ingo Jul 23 '13 at 08:30
  • 2
    While you quote a lot of stuff from the JLS, I don't see your point. You talk about the point in time when assert statements will get enabled during initialization. The original question wrote about an assert statement *triggering* initialization. Furthermore, your class `Baz` is top level as I understand things, since it is not nested. Inheritance doesn't play a role in the original question. So while most of what you write is correct, I don't see how it answers the question. I know you yourself said this was a partial answer, but I'm surprised it got accepted already. – MvG Jul 23 '13 at 09:08
  • @MvG The thing I was trying to show with inheritance was how the class initialization behaved wrt to `assert`. The timing about the `assert` being enabled/disabled (which again is part of class initialization) changes on which class executes it. It does *trigger* a part of initialization (i.e. as explained in your own answer about the flag it sets etc). And this is what the questions asks on how it influences initialization. Well about the nested part, i agree with you for which I said mine is a partial answer. About the acceptance, well I have no role in it :) – Jatin Jul 23 '13 at 09:22
  • Also for above said I think -1 is harsh. – Jatin Jul 23 '13 at 09:31
2

This is a top level class:

class TopLevel {
   ...
}

This is an assert statement:

assert( condition );

where condition is some boolean expression.

A is lexically nested into B, if it occurs inside the curly braces of B's definition. For example, fields, methods, static blocks are lexically nested in a class definition. Statements are lexically nested in methods or static blocks. Local definitions are nested in methods or blocks, that are itself nested in methods.

Hence an assert statement that is lexically nested in a top level class could be:

class A {
    static {
        assert ( 2+2 == 4 );
    }
}
Ingo
  • 36,037
  • 5
  • 53
  • 100
  • thank you for this simple answer, although I can't see how this could be executed before the class is initialized. as you've shown this, the assert statement is nested in static block, which will be executed **after** triggering initialization, am I right? – Line Jul 18 '20 at 16:57
2

I know how I would read this specification, but OpenJDK 1.7.0_40 does not behave as indicated, and neither does Oracle JDK 1.7.0_25.

A top level class is a class not nested inside any other. An assertion statement can occur in executable code, i.e. in a method, constructor or static initializer block. Most of these cases are handled by the other items: static methods are already covered, other methods as well as constructors fall under creation of an object of said class, and a static initializer block is part of the initialization process which is the result of any of the other events.

So the only way I can think of to have a lexically nested statement without triggering any of these cases would be via a nested class. E.g. something like this:

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

But if I run Outer.Nested.foo() with enabled assertions then I get the assertion error (so the statement got executed) but not the Outer initialized message. So the top level class was not initialized, even though a lexically nested assert statement got executed.

Either I'm misunderstanding the specification here, or the mentioned implementations don't follow it.

As for the rationale: I think that the main point of this requirement is the fact that enabling and disabling assertions is implemented via a hidden static (and iirc. final) field of the class. So when the assert statement gets executed, it has to check that field, hence that field has to be initialized, hence the class has to be initialized. But in the above code, the relevant field is likely that of Outer.Nested, not that of Outer itself. So it makes sense that Outer doesn't have to be initialized at that point. But apart from the above construct, I can not think of a case where the last rule would apply but none of the other rules.

MvG
  • 57,380
  • 22
  • 148
  • 276
  • I read the spec the same way, and, like you say, the Oracle JDK doesn't seem to follow it. Frustrating that the accepted answer to this question doesn't actually deal with this issue at all! – Matt R May 30 '14 at 15:29
2

You have linked to section 12.4.1 of the JLS for Java 7.

In java bug JDK-8043189 "12.4.1: Should asserts trigger initialization of outer classes?" there is a comment explaining that the last bullet of the list, which reads "T is a top-level class, and an assert statement (§14.10) lexically nested within T is executed" is simply wrong, and it had been erroneously inserted into the JLS back in version 6, and it stayed there until version 7 simply because nobody noticed it.

I found that the statement was still there in the JLS for Java 8, but it has finally been removed from paragraph 12.4.1 of the JLS for Java 9.

So, bottom line is, do not try to make any sense out of it, because there is none to be made; it is not a true statement, and it never was.

Mike Nakis
  • 56,297
  • 11
  • 110
  • 142