Java assert 키워드의 기능은 무엇이며 언제 사용해야합니까?
주장의 핵심 역할을 이해하기위한 실제 사례 는 무엇입니까 ?
( assertion 키워드를 통한) Assertion 은 Java 1.4에서 추가되었습니다. 코드에서 불변의 정확성을 확인하는 데 사용됩니다. 프로덕션 코드에서 트리거되지 않아야하며 버그 또는 코드 경로의 오용을 나타냅니다. 명령 의 -ea
옵션을 통해 런타임에 활성화 할 수 java
있지만 기본적으로 켜져 있지는 않습니다.
예 :
public Foo acquireFoo(int id) {
Foo result = null;
if (id > 50) {
result = fooService.read(id);
} else {
result = new Foo(id);
}
assert result != null;
return result;
}
원자력 발전소를 제어하는 프로그램을 작성해야한다고 가정 해 봅시다. 아무리 사소한 실수라도 치명적인 결과를 초래할 수 있으므로 코드 는 버그가 없어야합니다 (JVM이 인수를 위해 버그가 없다고 가정).
Java는 검증 가능한 언어가 아닙니다. 즉, 작업 결과가 완벽 할 것이라고 계산할 수 없습니다. 그 주된 이유는 포인터입니다. 포인터는 아무데도 또는 아무데도 가리킬 수 없으므로 최소한 합리적인 코드 범위 내에서는이 정확한 값으로 계산 될 수 없습니다. 이 문제를 감안할 때 코드가 전체적으로 정확하다는 것을 증명할 방법이 없습니다. 하지만 당신이 할 수있는 일은 최소한 모든 버그가 발생했을 때 발견한다는 것을 증명하는 것입니다.
이 아이디어는 DbC ( Design-by-Contract ) 패러다임을 기반으로합니다 . 먼저 메서드가 수행해야하는 작업을 수학적 정밀도로 정의한 다음 실제 실행 중에 테스트하여이를 확인합니다. 예:
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
return a + b;
}
이것이 잘 작동하는 것은 매우 분명하지만 대부분의 프로그래머는이 내부에 숨겨진 버그를 보지 못할 것입니다 (힌트 : 유사한 버그로 인해 Ariane V가 충돌했습니다). 이제 DbC는 함수의 입력과 출력을 항상 확인하여 올바르게 작동하는지 확인 해야한다고 정의 합니다. Java는 어설 션을 통해이를 수행 할 수 있습니다.
// Calculates the sum of a (int) + b (int) and returns the result (int).
int sum(int a, int b) {
assert (Integer.MAX_VALUE - a >= b) : "Value of " + a + " + " + b + " is too large to add.";
final int result = a + b;
assert (result - a == b) : "Sum of " + a + " + " + b + " returned wrong sum " + result;
return result;
}
이 기능이 이제 실패하면 알 수 있습니다. 코드에 문제가 있음을 알 수 있고, 문제가 어디에 있는지, 원인을 알 수 있습니다 (예외와 유사). 그리고 더 중요한 것은 잘못된 값으로 작동하는 추가 코드를 방지하고 제어하는 모든 항목에 잠재적으로 손상을 입힐 가능성이있는 경우 바로 실행을 중지하는 것입니다.
Java 예외는 유사한 개념이지만 모든 것을 확인하지 못합니다. 더 많은 검사를 원하면 (실행 속도를 희생하면서) 어설 션을 사용해야합니다. 그렇게하면 코드가 부풀려 지지만 결국 놀라 울 정도로 짧은 개발 시간에 제품을 제공 할 수 있습니다 (버그를 일찍 수정할수록 비용이 낮아집니다). 또한 코드 내부에 버그가 있으면이를 감지 할 수 있습니다. 버그가 빠져 나가 나중에 문제를 일으킬 수있는 방법은 없습니다.
이것은 여전히 버그가없는 코드에 대한 보장은 아니지만 일반적인 프로그램보다 훨씬 더 가깝습니다.
어설 션은 코드에서 버그를 잡는 개발 단계 도구입니다. 쉽게 제거 할 수 있도록 설계되었으므로 프로덕션 코드에 존재하지 않습니다. 따라서 어설 션은 고객에게 제공하는 "솔루션"의 일부가 아닙니다. 그들은 당신이 만드는 가정이 올바른지 확인하기위한 내부 점검입니다. 가장 일반적인 예는 null을 테스트하는 것입니다. 많은 메소드가 다음과 같이 작성됩니다.
void doSomething(Widget widget) {
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
이와 같은 메서드에서 종종 위젯은 null이 아니어야합니다. 따라서 null이면 추적해야 할 코드 어딘가에 버그가 있습니다. 그러나 위의 코드는이를 알려주지 않습니다. 따라서 "안전한"코드를 작성하려는 의도적 인 노력으로 버그를 숨기는 것입니다. 다음과 같은 코드를 작성하는 것이 훨씬 좋습니다.
/**
* @param Widget widget Should never be null
*/
void doSomething(Widget widget) {
assert widget != null;
widget.someMethod(); // ...
... // do more stuff with this widget
}
이렇게하면이 버그를 조기에 발견 할 수 있습니다. (계약에서이 매개 변수가 null이 될 수 없음을 지정하는 것도 유용합니다.) 개발 중에 코드를 테스트 할 때 어설 션을 켜야합니다. (그리고 여러분의 동료들에게도 그렇게하도록 설득하는 것도 종종 어렵습니다. 저는 매우 짜증이납니다.)
이제 일부 동료는 프로덕션에서 예외를 방지하기 위해 여전히 null 검사를 입력해야한다고 주장하면서이 코드에 반대 할 것입니다. 이 경우 주장은 여전히 유용합니다. 다음과 같이 작성할 수 있습니다.
void doSomething(Widget widget) {
assert widget != null;
if (widget != null) {
widget.someMethod(); // ...
... // do more stuff with this widget
}
}
이렇게하면 동료가 프로덕션 코드에 대해 null 검사가 있다는 사실에 만족할 것입니다. 그러나 개발 중에 위젯이 null 일 때 더 이상 버그를 숨기지 않습니다.
다음은 실제 예제입니다. 저는 두 개의 임의 값이 같은지 비교하는 메서드를 작성했습니다. 두 값 중 하나가 null 일 수 있습니다.
/**
* Compare two values using equals(), after checking for null.
* @param thisValue (may be null)
* @param otherValue (may be null)
* @return True if they are both null or if equals() returns true
*/
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = thisValue.equals(otherValue);
}
return result;
}
이 코드 equals()
는 thisValue가 null이 아닌 경우 메서드 작업을 위임합니다 . 그러나 equals()
메서드 equals()
가 null 매개 변수를 적절하게 처리 하여 계약을 올바르게 수행 한다고 가정합니다 .
동료가 내 코드에 반대하여 많은 클래스 equals()
에 null을 테스트하지 않는 버그가있는 메서드 가 있으므로이 메서드에 검사를 넣어야한다고 말했습니다. 이것이 현명한 지 또는 오류를 강제해야하는지 논쟁의 여지가 있습니다. 그래야 오류를 발견하고 수정할 수 있습니다.하지만 저는 동료에게 연기하고 null 검사를했습니다. 여기에 주석으로 표시했습니다.
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
} else {
result = otherValue != null && thisValue.equals(otherValue); // questionable null check
}
return result;
}
여기서 추가 확인 other != null
은 equals()
메서드가 계약에서 요구하는대로 null을 확인하지 못하는 경우에만 필요합니다 .
버그가 많은 코드를 코드베이스에 유지하는 지혜에 대해 동료와 무익한 토론에 참여하는 대신 코드에 두 가지 주장을 넣었습니다. 이러한 주장은 개발 단계에서 클래스 중 하나가 equals()
제대로 구현 되지 않는 경우 알려주 므로 수정할 수 있습니다.
public static boolean compare(final Object thisValue, final Object otherValue) {
boolean result;
if (thisValue == null) {
result = otherValue == null;
assert otherValue == null || otherValue.equals(null) == false;
} else {
result = otherValue != null && thisValue.equals(otherValue);
assert thisValue.equals(null) == false;
}
return result;
}
명심해야 할 중요한 사항은 다음과 같습니다.
어설 션은 개발 단계 도구 일뿐입니다.
단언의 요점은 코드뿐만 아니라 코드베이스에도 버그가 있는지 알려주는 것 입니다. (여기의 단언은 실제로 다른 클래스의 버그를 표시합니다.)
동료가 우리 수업이 제대로 작성되었다고 확신하더라도 여기에있는 주장은 여전히 유용 할 것입니다. null 테스트에 실패 할 수있는 새 클래스가 추가 될 것이며이 메서드는 해당 버그에 플래그를 지정할 수 있습니다.
개발시에는 작성한 코드가 어설 션을 사용하지 않더라도 항상 어설 션을 켜야합니다. 내 IDE는 새 실행 파일에 대해 기본적으로 항상이 작업을 수행하도록 설정되어 있습니다.
어설 션은 프로덕션에서 코드의 동작을 변경하지 않으므로 동료는 null 검사가 있고
equals()
메서드가 버그가 있어도이 메서드가 제대로 실행된다는 점에 만족합니다 .equals()
개발 과정에서 버그가있는 방법을 잡을 수있어서 기쁩니다 .
또한 실패 할 임시 어설 션을 넣어 어설 션 정책을 테스트해야합니다. 그러면 로그 파일이나 출력 스트림의 스택 추적을 통해 알림을받을 수 있습니다.
assert
키워드의 기능을 설명하는 좋은 답변 은 많지만 " assert
키워드를 실생활에서 언제 사용해야 합니까?"라는 실제 질문에 대한 답변은 거의 없습니다.
대답 은 거의 없습니다 .
개념으로서의 주장은 훌륭합니다. 좋은 코드를 많이 가지고 if (...) throw ...
문 (및 친척 등 Objects.requireNonNull
과 Math.addExact
). 그러나 특정 디자인 결정은 assert
키워드 자체 의 유용성을 크게 제한했습니다 .
assert
키워드 의 원동력 은 조기 최적화이며 주요 기능은 모든 검사를 쉽게 해제 할 수 있다는 것입니다. 실제로 assert
검사는 기본적으로 해제되어 있습니다.
그러나 프로덕션에서 불변 검사를 계속하는 것이 매우 중요합니다. 이는 완벽한 테스트 커버리지가 불가능하고 모든 프로덕션 코드에는 진단 및 완화에 도움이되는 버그가 있기 때문입니다.
따라서 if (...) throw ...
퍼블릭 메소드의 매개 변수 값을 확인하고 던지기 위해 필요한 것처럼를 사용하는 것이 선호되어야합니다 IllegalArgumentException
.
때로는 처리하는 데 원하지 않는 시간이 오래 걸리는 불변 수표를 작성하고 싶을 수 있습니다 (중요 할 정도로 자주 호출됩니다). 그러나 이러한 검사는 테스트 속도를 늦추므로 바람직하지 않습니다. 이러한 시간 소모적 인 검사는 일반적으로 단위 테스트로 작성됩니다. 그럼에도 불구하고 때때로 assert
이러한 이유로 사용 하는 것이 합리적 일 수 있습니다 .
assert
단순히 더 if (...) throw ...
깨끗하고 예쁘기 때문에 사용하지 마십시오 (그리고 나는 깨끗하고 예쁘기 때문에 큰 고통으로 말합니다). 스스로를 도울 수없고 애플리케이션이 시작되는 방법을 제어 할 수 있다면 자유롭게 사용 assert
하되 프로덕션에서 항상 어설 션을 활성화하십시오. 물론 이것이 제가하는 경향이 있습니다. 나는 롬복 어노테이션을 추진하고 assert
있다 if (...) throw ...
. 여기에서 투표하세요.
(Rant : JVM 개발자는 끔찍하고 조기에 최적화 된 코더였습니다. 그래서 Java 플러그인과 JVM에서 많은 보안 문제에 대해 들었습니다. 그들은 프로덕션 코드에 기본 검사와 주장을 포함하는 것을 거부했으며 우리는 계속해서 가격을 지불하십시오.)
다음은 가장 일반적인 사용 사례입니다. 열거 형 값을 켜고 있다고 가정합니다.
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
}
모든 사건을 처리하는 한 괜찮습니다. 그러나 언젠가 누군가가 당신의 enum에 fig를 추가하고 switch 문에 추가하는 것을 잊을 것입니다. 이것은 당신이 switch 문을 떠날 때까지 효과가 느껴지지 않기 때문에 잡기 까다로울 수있는 버그를 생성합니다. 그러나 다음과 같이 스위치를 작성하면 즉시 잡을 수 있습니다.
switch (fruit) {
case apple:
// do something
break;
case pear:
// do something
break;
case banana:
// do something
break;
default:
assert false : "Missing enum value: " + fruit;
}
어설 션은 사후 조건을 확인하고 사전 조건 "실패하지 않아야 함"을 확인하는 데 사용됩니다. 올바른 코드는 어설 션을 실패하지 않아야합니다. 트리거 될 때 버그를 표시해야합니다 (문제의 실제 위치에 가까운 위치에있는 것이 좋습니다).
어설 션의 예는 특정 메서드 그룹이 올바른 순서로 호출되는지 확인하는 것입니다 (예 :에서 hasNext()
이전 next()
에 호출 됨 Iterator
).
Java의 assert 키워드는 무엇을합니까?
컴파일 된 바이트 코드를 살펴 보겠습니다.
결론은 다음과 같습니다.
public class Assert {
public static void main(String[] args) {
assert System.currentTimeMillis() == 0L;
}
}
다음과 거의 동일한 바이트 코드를 생성합니다.
public class Assert {
static final boolean $assertionsDisabled =
!Assert.class.desiredAssertionStatus();
public static void main(String[] args) {
if (!$assertionsDisabled) {
if (System.currentTimeMillis() != 0L) {
throw new AssertionError();
}
}
}
}
어디 Assert.class.desiredAssertionStatus()
인 true
경우 -ea
그렇지 않으면 명령 행에 전달하고, 거짓된다.
우리는 System.currentTimeMillis()
그것이 최적화되지 않도록하기 위해 사용 합니다 assert true;
.
합성 필드가 생성되어 Java Assert.class.desiredAssertionStatus()
는로드시 한 번만 호출 하면되고 결과를 거기에 캐시합니다. 참조 : "정적 합성"의 의미는 무엇입니까?
다음을 통해 확인할 수 있습니다.
javac Assert.java
javap -c -constants -private -verbose Assert.class
Oracle JDK 1.8.0_45를 사용하여 합성 정적 필드가 생성되었습니다 ( "정적 합성"의 의미는 무엇입니까? 참조 ).
static final boolean $assertionsDisabled;
descriptor: Z
flags: ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
정적 이니셜 라이저와 함께 :
0: ldc #6 // class Assert
2: invokevirtual #7 // Method java/lang Class.desiredAssertionStatus:()Z
5: ifne 12
8: iconst_1
9: goto 13
12: iconst_0
13: putstatic #2 // Field $assertionsDisabled:Z
16: return
주요 방법은 다음과 같습니다.
0: getstatic #2 // Field $assertionsDisabled:Z
3: ifne 22
6: invokestatic #3 // Method java/lang/System.currentTimeMillis:()J
9: lconst_0
10: lcmp
11: ifeq 22
14: new #4 // class java/lang/AssertionError
17: dup
18: invokespecial #5 // Method java/lang/AssertionError."<init>":()V
21: athrow
22: return
결론 :
- there is no bytecode level support for
assert
: it is a Java language concept assert
could be emulated pretty well with system properties-Pcom.me.assert=true
to replace-ea
on the command line, and athrow new AssertionError()
.
A real world example, from a Stack-class (from Assertion in Java Articles)
public int pop() {
// precondition
assert !isEmpty() : "Stack is empty";
return stack[--num];
}
An assertion allows for detecting defects in the code. You can turn on assertions for testing and debugging while leaving them off when your program is in production.
Why assert something when you know it is true? It is only true when everything is working properly. If the program has a defect, it might not actually be true. Detecting this earlier in the process lets you know something is wrong.
An assert
statement contains this statement along with an optional String
message.
The syntax for an assert statement has two forms:
assert boolean_expression;
assert boolean_expression: error_message;
Here are some basic rules which govern where assertions should be used and where they should not be used. Assertions should be used for:
Validating input parameters of a private method. NOT for public methods.
public
methods should throw regular exceptions when passed bad parameters.Anywhere in the program to ensure the validity of a fact which is almost certainly true.
For example, if you are sure that it will only be either 1 or 2, you can use an assertion like this:
...
if (i == 1) {
...
}
else if (i == 2) {
...
} else {
assert false : "cannot happen. i is " + i;
}
...
- Validating post conditions at the end of any method. This means, after executing the business logic, you can use assertions to ensure that the internal state of your variables or results is consistent with what you expect. For example, a method that opens a socket or a file can use an assertion at the end to ensure that the socket or the file is indeed opened.
Assertions should not be used for:
Validating input parameters of a public method. Since assertions may not always be executed, the regular exception mechanism should be used.
Validating constraints on something that is input by the user. Same as above.
Should not be used for side effects.
For example this is not a proper use because here the assertion is used for its side effect of calling of the doSomething()
method.
public boolean doSomething() {
...
}
public void someMethod() {
assert doSomething();
}
The only case where this could be justified is when you are trying to find out whether or not assertions are enabled in your code:
boolean enabled = false;
assert enabled = true;
if (enabled) {
System.out.println("Assertions are enabled");
} else {
System.out.println("Assertions are disabled");
}
In addition to all the great answers provided here, the official Java SE 7 programming guide has a pretty concise manual on using assert
; with several spot-on examples of when it's a good (and, importantly, bad) idea to use assertions, and how it's different from throwing exceptions.
Assert is very useful when developing. You use it when something just cannot happen if your code is working correctly. It's easy to use, and can stay in the code for ever, because it will be turned off in real life.
If there is any chance that the condition can occur in real life, then you must handle it.
I love it, but don't know how to turn it on in Eclipse/Android/ADT . It seems to be off even when debugging. (There is a thread on this, but it refers to the 'Java vm', which does not appear in the ADT Run Configuration).
Here's an assertion I wrote in a server for a Hibernate/SQL project. An entity bean had two effectively-boolean properties, called isActive and isDefault. Each could have a value of "Y" or "N" or null, which was treated as "N". We want to make sure the browser client is limited to these three values. So, in my setters for these two properties, I added this assertion:
assert new HashSet<String>(Arrays.asList("Y", "N", null)).contains(value) : value;
Notice the following.
This assertion is for the development phase only. If the client sends a bad value, we will catch that early and fix it, long before we reach production. Assertions are for defects that you can catch early.
This assertion is slow and inefficient. That's okay. Assertions are free to be slow. We don't care because they're development-only tools. This won't slow down the production code because assertions will be disabled. (There's some disagreement on this point, which I'll get to later.) This leads to my next point.
This assertion has no side effects. I could have tested my value against an unmodifiable static final Set, but that set would have stayed around in production, where it would never get used.
This assertion exists to verify the proper operation of the client. So by the time we reach production, we will be sure that the client is operating properly, so we can safely turn the assertion off.
Some people ask this: If the assertion isn't needed in production, why not just take them out when you're done? Because you'll still need them when you start working on the next version.
Some people have argued that you should never use assertions, because you can never be sure that all the bugs are gone, so you need to keep them around even in production. And so there's no point in using the assert statement, since the only advantage to asserts is that you can turn them off. Hence, according to this thinking, you should (almost) never use asserts. I disagree. It's certainly true that if a test belongs in production, you should not use an assert. But this test does not belong in production. This one is for catching a bug that's not likely to ever reach production, so it may safely be turned off when you're done.
BTW, I could have written it like this:
assert value == null || value.equals("Y") || value.equals("N") : value;
This is fine for only three values, but if the number of possible values gets bigger, the HashSet version becomes more convenient. I chose the HashSet version to make my point about efficiency.
Assertion are basically used to debug the application or it is used in replacement of exception handling for some application to check the validity of an application.
Assertion works at run time. A simple example, that can explain the whole concept very simply, is herein - What does the assert keyword do in Java? (WikiAnswers).
Assertions are disabled by default. To enable them we must run the program with -ea
options (granularity can be varied). For example, java -ea AssertionsDemo
.
There are two formats for using assertions:
- Simple: eg.
assert 1==2; // This will raise an AssertionError
. - Better:
assert 1==2: "no way.. 1 is not equal to 2";
This will raise an AssertionError with the message given displayed too and is thus better. Although the actual syntax isassert expr1:expr2
where expr2 can be any expression returning a value, I have used it more often just to print a message.
To recap (and this is true of many languages not just Java):
"assert" is primarily used as a debugging aid by software developers during the debugging process. Assert-messages should never appear. Many languages provide a compile-time option that will cause all "asserts" to be ignored, for use in generating "production" code.
"exceptions" are a handy way to handle all kinds of error conditions, whether or not they represent logic errors, because, if you run into an error-condition such that you cannot continue, you can simply "throw them up into the air," from wherever you are, expecting someone else out there to be ready to "catch" them. Control is transferred in one step, straight from the code that threw the exception, straight to the catcher's mitt. (And the catcher can see the complete backtrace of calls that had taken place.)
Furthermore, callers of that subroutine don't have to check to see if the subroutine succeeded: "if we're here now, it must have succeeded, because otherwise it would have thrown an exception and we wouldn't be here now!" This simple strategy makes code-design and debugging much, much easier.
Exceptions conveniently allow fatal-error conditions to be what they are: "exceptions to the rule." And, for them to be handled by a code-path that is also "an exception to the rule ... "fly ball!"
Basically, "assert true" will pass and "assert false" will fail. Let's looks at how this will work:
public static void main(String[] args)
{
String s1 = "Hello";
assert checkInteger(s1);
}
private static boolean checkInteger(String s)
{
try {
Integer.parseInt(s);
return true;
}
catch(Exception e)
{
return false;
}
}
Assertions are checks which may get switched off. They're rarely used. Why?
- They must not be used for checking public method arguments as you have no control over them.
- They should not be used for simple checks like
result != null
as such checks are very fast and there's hardly anything to save.
So, what's left? Expensive checks for conditions really expected to be true. A good example would be the invariants of a data structure like RB-tree. Actually, in ConcurrentHashMap
of JDK8, there are a few such meaningful asserts for the TreeNodes
.
- You really don't want to switch them on in production as they could easily dominate the run time.
- You may want to switch them on or off during tests.
- You definitely want to switch them on when working on the code.
assert
is a keyword. It was introduced in JDK 1.4. The are two types of assert
s
- Very simple
assert
statements - Simple
assert
statements.
By default all assert
statements will not be executed. If an assert
statement receives false, then it will automatically raise an assertion error.
'developer tip' 카테고리의 다른 글
PHP에서 문자열을 숫자로 어떻게 변환합니까? (0) | 2020.10.03 |
---|---|
jquery를 사용하여 확인란을 선택 / 선택 취소 하시겠습니까? (0) | 2020.10.03 |
ConcurrentHashMap과 Collections.synchronizedMap (Map)의 차이점은 무엇입니까? (0) | 2020.10.03 |
작업중인 현재 셸을 확인하는 방법은 무엇입니까? (0) | 2020.10.03 |
단위 테스트에 대한 합리적인 코드 커버리지 %는 무엇이며 그 이유는 무엇입니까? (0) | 2020.10.03 |