Good day.
As part of the detailed development
of the “Java Core” distance education course, I am writing a series of publications and doing several translations of the most popular articles.
I also teach
Scala for Java Developers on the udemy.com online education platform (equivalent to Coursera / EdX).
Now I offer you for review my translation of
"Programming With Assertions" with some comments.
')
The original publication not only explains in detail the options for using the
assert keyword in Java and how support for this mechanism is implemented at the class load level, but is also a rather informal introduction to
Design-by-Contract .
Other articles and translations for the Java Core course Other articles and translations for Multicore Java programming PS The author admits that for him personally the Russian language is much more complicated than the Java language, will gratefully hear in a personal about all the errors noticed and will try to fix them as soon as possible.
Programming with statements (oracle.com: Programming With Assertions)An assert is a statement of the Java programming language that allows you to test your assumptions about a program. For example, if you write a method that calculates the speed of a particle, you can “assert” that the calculated speed is less than the speed of light.
Each statement contains a logical expression that you think will be true at the time of execution. Otherwise, the system will throw an exception. Checking that the logical expression is actually true, the statement (assert) confirms your assumptions (expectations) about the program's behavior, increasing the confidence that the program contains no errors.
Experience shows that writing statements in programming is one of the fastest and most effective ways to detect and correct errors. As an added benefit, assertions serve to document the internal workings of the program and enhance maintainability.
This article shows how to program with assertions. It covers the following topics:
IntroductionThe introduction of statements in the codeCompiling Files Using ClaimsEnable and Disable AssertionsCompatibility with existing programsDesign Frequently Asked QuestionsIntroduction
The assert operator has two forms. The first, simpler form is:
assert Expression1;
where
Expression 1 is a boolean expression. When the system checks an assertion, it evaluates
Expression 1 and if it is false (
false ), then the system throws
java.lang.AssertionError without a detailed error message.
The second form of statement is:
assert Expression1 : Expression2;
Where:
- Expression 1 is a logical expression.
- Expression 2 is an expression that has a value (cannot be a call to the void method).
Use this version of the assert statement to provide a detailed error message. The system passes the value of Expression
2 to the appropriate
AssertionError constructor to use the string representation of the value as a detailed error message.
Translator commentAssertError contains separate constructors for each data type (short and byte are automatically cast to int, no constructors were provided for them)
package java.lang; public class AssertionError extends Error { ... public AssertionError(Object detailMessage) {...} public AssertionError(boolean detailMessage) {...} public AssertionError(char detailMessage) {...} public AssertionError(int detailMessage) {...} public AssertionError(long detailMessage) {...} public AssertionError(float detailMessage) {...} public AssertionError(double detailMessage) {...} ... }
This allows you to
use an expression of any primitive or reference type as
Expression 2 :
public class App { public static void main(String[] args) { assert args != null : 1; assert args != null : 1.0; assert args != null : false; assert args != null : "Hello!"; assert args != null : new int[] {10, 20, 30}; }
or calls to arbitrary methods that return something
public class App { public static void main(String[] args) { assert args != null : f0(); assert args != null : f1(); assert args != null : f2(); assert args != null : f3(); assert args != null : f4(); } private static byte f0() {return 0;} private static double f1() {return 0.0;} private static boolean f2() {return true;} private static String f3() {return "Hello!";} private static int[] f4() {return new int[] {10, 20, 30};} }
but not calling a method that returns void
public class App { public static void main(String[] args) { assert args != null : f(); // } private static void f() {} } >> COMPILATION ERROR: 'void' type is not allowed here
The purpose of such an error message is to record and report information about the cause of the violation of the claim. The message should allow you to diagnose and, ultimately, fix the error that caused the statement to fail. Note that the message is NOT an error message for the user. In general, there is no need to make these messages understandable on their own, or to internationalize them (translate into the user's language). The detailed message should be interpreted in the context of a complete stack trace in conjunction with the source code, which contains a faulty statement.
Like all unhandled exceptions, assertion failures, as a rule, contain a stack trace (stack trace) with the file number and the line from which they were thrown. The second form of approval should be used in preference to the first only when the program has some additional information that can help diagnose the failure. For example, if
Expression 1 assumes a relationship between two variables x and y, then the second form should be used. Under these conditions, a reasonable variable for
Expression 2 will be
"x:" + x + ", y:" + y .
Translator commentFor example, the following example is meant.
class App { public static void f(){ int x, y;
just throw an
AsertionError in case of an assertion violation (x> y).
But such an example
class App { public static void f(){ int x, y;
will throw an
AsertionError with a message, say,
"x: 0, y: 123", which will allow the programmer to analyze specific invalid values of x and y at the time of the calculation.
In some cases,
Expression 1 can be expensive to calculate. For example, suppose that when writing a method that searches for the minimum element in an unsorted list, an assertion is added to verify that the selected element is indeed minimal. In this case, the verification statement will be as “expensive” as the execution of the method itself. In order to make sure that assertions do not degrade application performance, assertions can be turned on or off at the time the program starts. By default, they are disabled. Disabling an assertion completely eliminates a performance loss. Once they are disabled, they are equivalent to an empty operator in semantics and performance. For more information, see:
Enabling and disabling assertions .
Translator commentIn the ArrayUtils.min (int []) method, two postinvariant checks and both are comparable in complexity to the complexity of finding the minimum element (O (N))
public class ArrayUtils { public static int min(int[] array) { int min; // // min - array assert checkMin(array, min); assert checkContains(array, min); return min; } // , private static boolean checkMin(int[] array, int min) { for (int elem : array) { if (elem < min) { return false; } } return true; } // , "" private static boolean checkContains(int[] array, int min) { for (int elem : array) { if (elem == min) { return true; } } return false; } }
Translator commentthe empty operator apparently means the semicolon operator (;)
public class App { public static void main(String[] args) { ;;;; ;;;;;;;; ;;; ;; ;;; ;;;;;;;;;; ;; ;; ;;;;;;;; ;;;; for (int k = 0; k < 10; k++) { ; } } }
Adding the
assert keyword to Java has implications for existing code. For more information, see
Compatibility with Existing Programs .
The introduction of statements in the code
There are many situations in which it is useful to use statements, including:
There are also situations where you should not use them:
Translator commentSide effect (side effects) - any action that changes the "visible world."
The implication is simply to perform
tmp x = 1 + 2 + 3;
It does not change the “visible world” (if no one else in the program uses the tmp identifier), although the “invisible world to the programmer” may have changed (bytecodes are loaded, memory in the stack is allocated for a local variable, the processor added 1, 2 and 3)
Performance
System.out.println ("Hello!");
changes the "visible world" (has side effects) - a program with such a line obviously prints "more" to the console, but still it does not affect the program below - we do not change the read data.
But the line
tmp = 2 * tmp;
affects the program below (has side effects) if below we read and use the value of the variable tmp.
Internal invariants
Before statements became available in the language, many programmers used comments to “express” their assumptions about program behavior. For example, to reveal their assumptions about the else-section in a long chain if-elseif-elseif- .... Previously wrote something like this:
if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else{
Now you
should use a statement instead of every comment with an "obviously true statement .
" For example, it is worth rewriting the previous code like this:
if (i % 3 == 0) { ... } else if (i % 3 == 1) { ... } else { assert i % 3 == 2 : i; ... }
It is worth noting that the statement in the example above can fail if
'i' is negative, since the% operator calculates the remainder from the division (remainder), which can be negative.
Translator commentexample
Another candidate for using assertions is the default-section switch statement. The absence of a default section usually indicates that the programmer is confident that one of the case sections will always be selected. The assumption that a variable will have one of a small number of values is an invariant and should be tested with an assertion. Suppose the following switch statement appears in a program that deals with a card game:
switch(suit) { caseSuit.CLUBS: // ... break; caseSuit.DIAMONDS: // ... break; caseSuit.HEARTS: // ... break; caseSuit.SPADES: // ... }
Most likely this indicates the assumption that the suit type variable will have one of four values. To test this assumption, add the following default section:
default: assert false : suit;
If the variable set takes on a different value and the assertions are included, the assertion will fail and throw an
AssertionError exception.
An acceptable option would be:
default: throw new AssertionError(suit);
This idiom offers protection, even in the case of disabled statements. And this additional protection costs nothing: the
throw statement is executed only if the program fails. In addition, this option is valid under certain circumstances, when not
assert . Let's say if a method's method returns a value, each case in the switch statement contains a return and there is no return result after the switch, then the first option will result in a syntax error.
Translator commentThis program compiles
public class App { public static int twice(int arg) { switch (arg) { case 0: return 0; case 1: return 2; case 2: return 4; case 3: return 6; default: throw new AssertionError(arg); } } }
but this one is not
class App { public static int twice(int arg) { switch (arg) { case 0: return 0; case 1: return 2; case 2: return 4; case 3: return 6; default: assert false : arg; } } } COMPILATION ERROR: Missing return statement
Control flow invariants
In the previous example, not only the invariant is tested, it also tests the assumption about the flow of execution. The author of the original switch statement probably assumes not only that the
suit variable will always have one of four values, but also the fact that we “go into” one of the case sections. This points to another area where you need to use assertions:
place an assertion anywhere that NEVER is supposed to be reached . You must use the following statement:
assert false;
Suppose you have the following method:
void foo() { for (...) { if (...) {return;} ... }
You need to replace the final comment so that the code looks like this:
void foo() { for (...) { if (...) {return;} } assert false;
Note: Use this method with caution. If the status is determined to be inaccessible according to the Java specification, then you will get a compile-time error if you try to place a statement there. Again, an acceptable alternative is to simply throw an AssertionError.
Translator commentAccording to the Java specification, the compiler is instructed to check the operators for "unavailability". In case if “unavailable” operators are detected, a compilation error occurs. Prerequisite: a program with operators that are never executed — most likely the result of a programmer's error
public class App { public static void main(String[] args) { return; return;
The inspection rules are rigidly spelled out and do not include all possible situations. Let's say it is so easy to "cheat" the compiler:
public class App { public static void main(String[] args) { if (true) {return;} return;
Preconditions (preconditions), postconditions (postconditions) class invariants (class invariants)
While assert does not provide a full-fledged design-by-contract opportunity, it can help support an informal design-by-contract programming style. This section shows how to use statements for situations such as
Preconditions (preconditions)
In accordance with agreement, preconditions in open (public) methods produce checks that throw particular specific exceptions. For example:
public void setRefreshRate(int rate) { // if (rate <= 0 || rate > MAX_REFRESH_RATE) throw new IllegalArgumentException("Illegal rate: " + rate); setRefreshInterval(1000 / rate); }
This agreement has not changed when adding the operator assert.
Do not use assertions to check the parameters of the public method The use of assertions in this case is inappropriate, since the method guarantees that it will always check the argument. He should check whether the statements are independent. In addition, assert constructions cannot be specified to throw out exceptions of the specified type. It can only generate an
AssertionError .
However, you can use statements to test the preconditions of the non-public method, which you calculate will be true, regardless of what the client does with the class. For example, the statement is appropriate in the following auxiliary method, which is called by the previous public method:
private void setRefreshInterval(int interval) {
Note: this statement will fail if
MAX_REFRESH_RATE is more than 1000 or the client selects an update rate of more than 1000. This, in fact, indicates an error in the library!
Lock-status preconditions (lock-status preconditions)
Classes intended for multi-threaded use often have non-public (non-public) methods with preconditions such that some locks are captured or free. It's not uncommon to see something like this:
privateObject[] a; public synchronized int find(Object key) { return find(key, a, 0, a.length); } / / - c private int find(Object key, Object[] arr, int start, int len) { ... }
A static method called
holdsLock (Object) has been added to the
Thread class to test whether the current thread has a lock on the specified object. This method can be used in conjunction with a statement to add a comment, as shown in the following example:
/ / : private int find(Object key, Object[] arr, int start, intlen) { assert Thread.holdsLock(this);
Please note, it is also possible to write a statement, which consists in the fact that some lock is
not captured !
Postconditions
You can check the postcondition using assertions in both open (public) and closed (nonpublic) methods. For example, the following public method uses a statement to test the postcondition:
Returns the BigInteger whose value (this
-1 mod m).
public BigInteger modInverse(BigInteger m) { if (m.signum <= 0) { throw new ArithmeticException("Modulus not positive: " + m); } ...
Translator commentA little algebra: in the residue ring on the base N (Z N ) every non-zero element x has (and a unique) inverse element, if x is mutually simple with N. If x is not mutually simple with N, then x has no inverse element.
Example # 1 (backward in ring Z 5 ):
for 1 - reverse = 1, 1 * 1 mod 5 = 1
for 2 - reverse = 3, 2 * 3 mod 5 = 1
for 3 - reverse = 2, 3 * 2 mod 5 = 1
for 4 - reverse = 4, 4 * 4 mod 5 = 1
Example # 2 (inverse in ring Z 6 ):
for 1 - reverse = 1, 1 * 1 mod 6 = 1
for 2 - no return
for 3 - no return
for 4 - no return
for 5 - reverse = 5, 5 * 5 mod 6 = 1
232.xSometimes it is necessary to save some data before performing the calculations related to the postcondition check. You can do this with two statements and a simple inner class that maintains the state of one or more variables so that they can be checked (or rechecked) after calculations. For example, suppose you have a piece of code that looks like this:
void foo(int[] array) {
Here's how to change the above method in order to turn the textual statement of a postcondition into a “functional” one: void foo(final int[] array) { // (inner) class DataCopy { private int[] arrayCopy; DataCopy() { arrayCopy = (int[]) array.clone();} boolean isConsistent() {return Arrays.equals(array, arrayCopy);} } DataCopy copy = null; // ; assert ((copy = new DataCopy()) != null); ... // array // , array assert copy.isConsistent(); }
You can easily generalize this approach to save more than one data element and to test assertions of arbitrary complexity.One could try to replace the first statement (which is performed solely for a side effect) with the following, more expressive: copy = new DataCopy();
Do not do this. The second example will copy the array regardless of whether the statements are included or not. This would violate the principle that approvals should not contribute any cost if they are disabled.Class invariants
Class invariants are one of the types of internal invariants that persist for each instance of a class all the time, except for the moments of transition from one consistent state to another. A class invariant can indicate relationships between different attributes, and must be true before and after the completion of any method. Suppose you implement a balanced tree data structure of some kind. The class invariant can be like this: the tree is balanced and correctly ordered.The approval mechanism does not impose any particular style for checking invariants. However, it is sometimes convenient to combine expressions that check the necessary constraints into a single internal method that can trigger a statement. In the continuation of the balanced tree example, it would be advisable to implement a private method that checks that the tree is really balanced in accordance with the requirements of the data structure:
Since this method checks the constraint that should be true before and after the execution of any method, each public method and constructor must contain the following line immediately before returning it: assert balanced();
As a rule, there is no need to place similar checks in the header of each open method, only if the data structure is not implemented through native methods. In this case, it is possible that a memory corruption bug may damage the data structure outside the Java heap (“native peer”) between method calls. An assertion failure at the beginning of the method will indicate that this kind of memory corruption has occurred.It may also be advisable to enable checking of class invariants at the beginning of each class method, if the state of the class can be changed by other classes. It is still better to use such a design of classes that the state is not directly visible to other classes!Translator comment« » ( ) « » . . «» , «» - , .
"Advanced" technology
The following sections will discuss topics that apply only to devices with limited resources and systems in which approvals should not be disabled. If you are not interested in this topic, go to the next section, Compiling Files Using Assertions .Eliminating Assertions from Class Files
Programmers developing applications for devices with limited resources may wish to completely eliminate assertions from class files. Although this makes it impossible to activate assertions in principle, it reduces the size of the class file, which can lead to faster loading of classes. In the absence of a high-quality JIT compiler, this can lead to a decrease in the required memory and an increase in performance at runtime.The approval mechanism does not offer direct support for removing them from class files. Statements, however, can be used in conjunction with the “conditional compilation” idiom. This approach is described in the Java specification and allows the compiler to eliminate all “references” to statements in the generated class files: static final boolean asserts = ... ;
Translator comment« » .
:
«An optimizing compiler may realize that the statement… will never be executed and may choose to omit the code for that statement from the generated class file ...»
( , ) javac «optimizing compiler»,
public class App { static final boolean asserts = false; public static void main(String[] args) {
public class App { static final boolean asserts = false; public static void main(String[] args) {
How to require inclusion of statements
Programmers of some critical systems may want their code to work exclusively with assertions included. The following approach based on static initialization prevents class initialization if its assertions have been disabled: static { boolean assertsEnabled = false; assert assertsEnabled = true; // !!! if (!assertsEnabled) throw new RuntimeException(" !!!"); }
Place this static initializer at the top of your class declaration.Translator comment« » , — . Those.
class App { static int x = f(); static { System.err.println("static{}"); } static int y = g(); static { boolean assertsEnabled = false; assert assertsEnabled = true; if (!assertsEnabled) throw new RuntimeException(); } public static void main(String[] args) {} static int f() { System.err.println("f()"); return 0; } static int g() { System.err.println("g()"); return 0; } } >> f() >> static{} >> g() >> Exception in thread "main" java.lang.ExceptionInInitializerError >> ...
.
Compiling Files Using Claims
In order for the javac compiler to accept code that contains assertions, you must use the -source 1.4 command line parameter , as in the following example: javac -source 1.4 MyClass.java
This flag is necessary in order not to cause a source code compatibility issue.Translator commentjavac (-source -target)javac -source 1.3 -target 1.4class-. , . Those. , Java 5 Java 1.3, - Java 1.4.
1.3 , , assert , generics, varargs,…
class- 1.4 , , class- Java 1.4.
class- (minor_version, major_version)
ClassFile { ... u2 minor_version; u2 major_version; ... }
Oracle's Java Virtual Machine implementation in JDK release 1.0.2 supports class file format versions 45.0 through 45.3 inclusive. JDK releases 1.1.* support class file format versions in the range 45.0 through 45.65535 inclusive. For k ≥ 2, JDK release 1.k supports class file format versions in the range 45.0 through 44+k.0 inclusive.
Enable and Disable Assertions
By default, assertions are disabled at run time. We have two command line switches.To enable assertions at various levels of detail, use -enableassertions or -ea . To disable assertions at different levels of detail, use -disableassertions or -da . You specify the details of the flag arguments:- no arguments
Enables or disables assertions in all classes except system - packageName ...
Enables or disables assertions in the specified package and in all subpackages. - ...
Enable or disable assertions in the untitled package in the current working directory. - className
Enables or disables assertions in the specified class.
For example, the following command launches BatTutor with assertions enabled only for the com.wombat.fruitbat package and its subpackages: java -ea:com.wombat.fruitbat... BatTutor
If the command line contains several such "switches", they are processed in order before loading any class. For example, the following command starts the BatTutor program with the com.wombat.fruitbat package assertions enabled, but with com.wombat.fruitbat.Brickbat disabled for the class : java -ea:com.wombat.fruitbat... -da:com.wombat.fruitbat.Brickbat BatTutor
The above command line flags apply to all class loaders with one exception. This is a system ClassLoader. The exception concerns switches with no arguments, which (as stated above) do not apply to system classes. This behavior makes it easy to include assertions in all classes except for system classes, which is usually desirable.To enable assertions in all system classes, use a different flag: -enablesystemassertions or -esa . Similarly, to disable assertions in system classes use -disablesystemassertions or -dsa .For example, the following command starts the BatTutor program with assertions included in both the system classes and the com.wombat.fruitbat package and its subpackages: java -esa -ea:com.wombat.fruitbat...
The status of an assertion for a class (on or off) is set at the time of class initialization and does not change more. However, there is a separate case that requires special handling. It is possible, though usually undesirable, to execute methods or constructors before initialization. This can occur if the class hierarchy contains cyclicality in the static initialization procedure.If an assertion is executed before the class is initialized, then the execution should behave as if the assertions were included in the class. This topic is discussed in detail in the Java specification.Translator comment:
An assert statement that is executed before its class has completed initialization is enabled.
This rule is motivated by a case that demands special treatment. Recall that the assertion status of a class is set no later than the time it is initialized. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization, as in the following example:
public class Foo { 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 = true; System.out.println("Asserts " + (enabled ? "enabled" : "disabled")); } } >> Asserts enabled >> Asserts disabled
Invoking Baz.testAsserts() causes Baz to be initialized. Before this can happen, Bar must be initialized. Bar's static initializer again invokes Baz.testAsserts(). Because initialization of Baz is already in progress by the current thread, the second invocation executes immediately, though Baz is not initialized (§12.4.2).
Because of the rule above, if the program above is executed without enabling assertions.
Compatibility with existing programs
Adding the assert keyword to Java does not cause any problems with existing binaries ( .class files). If you try to compile an application that uses assert as an identifier, then you will receive a compiler warning or an error message. In order to facilitate the transition from a world where assertion is a valid identifier to a world where this is not the case, the compiler supports two modes of operation in this version:- Source mode 1.3 (default) - the compiler accepts programs that use assert as an identifier, but gives warnings. In this mode, programs are not allowed to use a statement.
- Source mode 1.4 - the compiler generates an error message if the program uses assert as an identifier. In this mode, programs are allowed to use a statement.
Unless you specifically request source mode 1.4 with the -source 1.4 flag , the compiler works in source mode 1.3. If you forget to use this flag, programs that use assertions will not compile. The decision to force the compiler to use the old semantics as the default behavior (that is, allowing assert to be used as an identifier) was made for the most favorable compatibility mode. Source mode 1.3 is likely to work for a long time.Translator comment( Java 1.4), . Those. javac JDK 7 JDK 8 , -source 1.3
Design and Frequently Asked Questions
This section contains frequently asked questions regarding the internal design of assertions.General issuesCompatibilitySyntax and semanticsAssertionError classEnabling and disabling assertionsGeneral issues
Why provide the possibility of approval, given that it is possible to program assertions in the top of the Java programming language, without special support?Although custom implementations are possible, they are often either ugly (requiring an if for each statement) or ineffective (condition evaluation, even if statements are disabled). In addition, each special implementation in its own way enables and disables assertions, which reduces the usefulness of these implementations, especially in debug mode. As a result of these flaws, assertions have never been part of the culture of Java programmers. Adding support for assertions to the platform has a good chance of rectifying this situation.Why is the introduction of this tool carried out through the expansion of the language, and not in the form of a library?We recognize that changing the language is a serious step and should not be taken lightly. Approach through the library was considered. However, it is considered important that the cost of fulfilling statements can be neglected if they are turned off. In order to achieve this in the case of the library, the programmer is forced to hard-code each statement as an if-operator. Many programmers would not do this. They will either omit the if operator and performance will suffer, or they will completely ignore the assertions. Note also that the claims are contained in the original Java specification from James Gosling. Claims have been removed from the Oak specification due to lack of time for satisfactory development and implementation.Translator commentJava Oak . :).
-- (design-by-contract) , , Eiffel?We considered this possibility, but we could not convince ourselves that we could “graft” it to Java without massive changes in the platform’s libraries and a massive backward-compatibility violation between the old and new libraries. In addition, we were not sure that such an opportunity would be able to preserve the simplicity of the language, which is a distinctive feature of Java. In the end, we came to the conclusion that a simple conditional statement (boolean assertion facility) would be a fairly understandable decision and much less risky. It is worth noting that the addition of conditional approval to the language does not preclude the addition of full support for the Development-by-Contract (design-by-contract) sometime in the future.A simple statement really allows the use of a limited form of Development-on-Contract. The statement (assert) is suitable for checking internal (nonpublic) preconditions, postconditions, and invariants of classes. An external (public) precondition check can still be performed by checks inside methods that lead, in particular, to documented exceptions, such as IllegalArgumentException and IllegalStateException.In addition to conditional statements (boolean assertions), why not provide an assert-like construct to disable the execution of a block of code if assertions are disabled?Providing such a construction will encourage programmers to make complex inline statements, although it is better to make them into separate methods.Compatibility
Wouldn't a new keyword be a compatibility issue with existing programs that use 'assert' as an identifier?Yes, at the source file level (binary files of classes using 'assert' as an identifier will continue to work correctly.) To ease the transition, we implemented a strategy according to which developers can continue to use 'assert' as an identifier during the transition period.Does this tool not lead to the appearance of class-files that can not be run on the old JRE?Yes, leads. The class files will contain calls to new class methods ClassLoader and Class such as desiredAssertionStatus (). If the class file containing the calls to these methods is executed by the old JRE (whose ClassLoader class does not have such methods), the program will not be executed and will throw in NoSuchMethodError . This is the case when programs using new language features are not compatible with older versions.Syntax and semantics
Why are primitive types allowed in Expression 2 ?There is no compelling reason for limiting the type of this expression. Allowing arbitrary types provides convenience for developers who, for example, want to associate unique integer code with each statement. In addition, it makes the expression “feel” like an argument to System.out.println (...) that looks desirable.Class AssertionError
When AssertionError is generated by a statement that does not contain Expression 2 , why does the program not report the status of the statement in a detailed text message, for example, “height <maxHeight” ?Although in some cases “out-of-the-box usefulness” will increase, this does not justify adding all these string constants to class files and class representations of runtime (runtime images).Why doesn't AssertionError provide access to the object that threw this exception? Similarly, why not pass an arbitrary object from the statement to the AssertionError constructorinstead of an error message (string)?Access to these objects will encourage programmers to try to restore the system after a failure in the statement, which is contrary to the purpose of this language feature.Why not provide contextual access methods (such as getFile () , getLine () , getMethod () ) for AssertionError?This facility is already provided for Throwable . Therefore, it can be used for all exceptional situation (throwables), and not only for assertion errors. We have this method in Throwable.getStackTrace () to provide this functionality.Translator comment public class App { public static void main(String[] args) { try { f(); } catch (Throwable t) { StackTraceElement[] stack = t.getStackTrace(); StackTraceElement frame = stack[0]; System.out.println("FileName: " + frame.getFileName()); System.out.println("ClassName: " + frame.getClassName()); System.out.println("MethodName: " + frame.getMethodName()); System.out.println("LineNumber: " + frame.getLineNumber()); } } public static void f() { throw new RuntimeException(); } } >> FileName: App.java >> ClassName: App >> MethodName: f >> LineNumber: 16
Why is AssertionError a subclass of Error rather than a RuntimeException ?That's a moot point. A group of experts discussed it and concluded that Error is a more suitable ancestor in order to save programmers from trying to restore the program after AssertionError . In general, it is rather difficult or even impossible to localize the source of the assert error. Such a failure means that the program “moves in an unknown direction” (“outside of known space”) and attempts to continue execution are likely to be disastrous. In addition, coding agreements (convention) require that methods specify most runtime exceptions (meaning the heirs of Exception , but not Error) which they can throw away ( @throws in javadoc). It hardly makes sense to include circumstances in the specification of a method under which it can generate assert errors. Such information may be considered as an implementation detail, which may vary from implementation to implementation and from release to release.Enable and Disable Assertions
Why do commands that enable and disable assertions use package-as-tree semantics instead of more traditional package semantics?Control over the hierarchy can be useful, since programmers actually use packages as hierarchical structures for organizing their code. For example, package-as-tree semantics allows you to enable or disable statements in all Swing for a single action.Translator commentJava, ( ).
: .
Those.
aaa.bbb.ccc «»
aaa.bbb xxx.yyy.zzz . (public, protected, (package private), private) : . « ».
Swing
javax.swing.* javax.swing.border.* javax.swing.colorchooser.* javax.swing.event.* javax.swing.filechooser.* javax.swing.plaf.* javax.swing.plaf.basic.* javax.swing.plaf.metal.* javax.swing.plaf.multi.* javax.swing.plaf.nimbus.* javax.swing.plaf.synth.* javax.swing.table.* javax.swing.text.* javax.swing.text.html.* javax.swing.text.html.parser.* javax.swing.text.rtf.* javax.swing.tree.* javax.swing.undo.* javax.swing.* javax.swing.*
--
javax.swing.* .
Why does setClassAssertionStatus () return a boolean instead of throwing an exception if it is called too late to set the status of the statement (that is, if the class has already been initialized)?No action (except, perhaps, a warning message) is necessary or desirable if the flag is set too late. The exception seems unnecessarily heavy.Why not overload one method name instead of using two: setDefaultAssertionStatus () and setAssertionStatus () ?Clarity in naming methods is done for the common good. Overloading of methods tends to cause confusion.Why not customize the desiredAssertionStatus semanticsso, to make it more “programmer friendly”, returning the current state of the assertion statement if the class is already initialized?It is not yet clear whether any benefit to the resulting method. This method is not intended for use by an application programmer and it seems impractical to make it slower and more difficult than necessary.Why is there no RuntimePermission to prevent applets from turning on / off assertions?Although there is no reason for the applet to call any of the ClassLoader methodsfor modifying the on / off status of assertions, allowing them to make such a call seems harmless. In the worst case, an applet can create a lightweight version of the denial-of-service attack by including assertions in classes that still need to be initialized. In addition, applets can only affect the status of class assertions that should be loaded by class loaders that applets can access. And here already exists RuntimePermission to prevent the ability to access the class loader ( getClassLoader ) for unreliable code.Translator comment«» Java.
java.lang.SecurityManager ( — , — ) RuntimePermission , — .
// "" public class App { public static void main(String[] args) { while (true); } }
// - public class App { public static void main(String[] args) { new Thread(new Runnable() { public void run() { System.exit(123); } }).start(); while (true); } } >> Process finished with exit code 123
// System.exit(123); // ... ""! public class App { public static void main(String[] args) { System.setSecurityManager(new SecurityManager() { public void checkExit(int status) { throw new SecurityException("You haven't permission to exit."); } }); new Thread(new Runnable() { public void run() {System.exit(123);} }).start(); while (true); } }
/ ,
, .
public class App { public static void main(String[] args) { ClassLoader loader = App.class.getClassLoader(); loader.setDefaultAssertionStatus(true); loader.setPackageAssertionStatus("", true); } }
SecurityException
grant { permission java.lang.RuntimePermission "getClassLoader"; };
java.policy
Why not provide a construct that will query the assertion status of the "surrounding" class?Such a construction will push people to build a complex code of statements. And we see this as a bad decision. In addition, if you need to, use the following construct over the current API: boolean assertsEnabled = false; assert assertsEnabled = true;
Why does an assert , which is executed before class initialization, behave as if statements were included?Very few programmers know that class constructors and methods can work before it is initialized. When this happens, it is likely that class invariants have not yet been created, which can lead to serious and subtle errors. Any statement that is executed in this state will most likely not work, warning the programmer about the problem. Thus, it is generally useful for the programmer to fulfill all the statements that the code encounters in such an unusual situation.Contacts
I do Java online training (here
are the programming courses ) and publish some of the training materials as part of the reworking
of the Java Core course . You can see videos of lectures in the audience on the
youtube channel , perhaps the video of the channel is better organized in
this article .
skype: GolovachCourses
email: GolovachCourses@gmail.com