생성자(Constructor)란 클래스 초기화를 위한 특별한 메소드이다. 일반적으로 메소드는 객체 생성시 자동으로 호출되지 않지만 생성자는 객체 생성시 자동으로 호출되어 객체가 프로그램에서 사용될 수 있도록 메모리 할당, 변수 초기화 등의 여러 작업을 한다. 생성자의 특징을 정리하면 다음과 같다.
생성자의 특징을 살펴봤으니 이제 소스 코드를 통해 생성자를 선언하는 방법을 살펴보도록 하겠다.
위의 소스 코드에는 두 개의 생성자가 선언되어 있다. 첫 번째 생성자는 어떤 기능도 수행하지 않는 생성자이고 두 번째 생성자는 생성자의 매개 변수의 값을 ConTest 클래스의 String 변수 name 에 할당하는 생성자이다. 이 코드를 봤을 때 생성자의 형식은 리턴형이 선언되어 있지 않다는 것을 제외하고는 메소드와 매우 유사하다는 것을 알 수 있다. 이와 같이 선언된 생성자를 호출하기 위해서는 new 연산자를 사용해야 한다. 생성자와 유사한 메소드는 new 연산자가 아닌 객체를 생성한 후 객체의 참조를 통해 해당 메소드를 호출하면 되지만 생성자는 이와는 다르게 new 연산자에 의해 자동적으로 호출된다.
객체는 상태와 행동을 표현하기 위한 변수와 메소드로 구성되며 변수는 객체의 상태 정보를 저장하기 위해 사용되고 메소드는 상태를 얻거나 변경하기 위해 사용된다.
시그니처(Signature)는 메소드의 리턴형(Return Type), 메소드명(Method Name), 매개 변수(Parameter)를 말한다.
변수에 인스턴스 변수와 클래스가 변수가 있듯이 메소드에도 인스턴스 메소드(Instance Method)와 클래스 메소드(Class Method)가 있다.
인스턴스 메소드는 static 제한자가 선언되지 않은 메소드로서 보통 메소드라고 하면 인스턴스 메소드를 가리키며 클래스 메소드는 static 제한자가 선언된 메소드로서 정적 메소드(Static Method)라고도 한다. 결국 인스턴스 메소드와 클래스 메소드의 차이는 static 제한자가 있느냐 없느냐이기 때문에 메소드의 차이는 static 제한자의 특성에 따른다고 볼 수 있다. 일단 이 메소드들이 어떻게 생겼는지 소스 코드를 보도록 하자.
인스턴스 메소드(Instance Method)
public class Test {
public int add(int a, int b) {
return a + b;
}
}
클래스 메소드(Class Method)
public class Test {
public static int add(int a, int b) {
return a + b;
}
}
static 제한자에 대해서는 [4.5 static 제한자(static Modifier)]에 자세히 나와 있으니 이를 참고하기로 하고 여기서는 인스턴스 메소드와 클래스 메소드의 특징에 대해서 간단히 정리해보도록 하자.
인스턴스 메소드(Instance Method)
1. static 제한자를 선언하지 않은 메소드를 말한다.
2. 인스턴스 메소드는 인스턴스 변수와 인스턴스 메소드에 바로 접근할 수 있다.
3. 인스턴스 메소드는 클래스 변수와 클래스 메소드에 바로 접근할 수 있다.
클래스 메소드(Class Method)
1. static 제한자를 선언한 메소드를 말한다.
2. 클래스 메소드는 클래스 변수와 클래스 메소드에 바로 접근할 수 있다.
3. 클래스 메소드에서 인스턴스 변수와 인스턴스 메소드에 접근하기 위해서는 해당 객체의 레퍼런스가 필요하다.
4. 클래스 메소드에서는 this 키워드를 사용할 수 없다.
※ 우리가 이전에 작성했던 main 메소드가 클래스 메소드의 대표적인 예이다.
3.7.2 main 메소드
자바에는 프로그램 시작을 위해 반드시 작성해야 하는 메소드가 있다. 이 메소드는 main 메소드로서 항상 정해진 형식으로 선언해야 한다. 그렇지 않을 경우 자바가상머신은 main 메소드를 인식하지 못해 프로그램을 시작하지 못한다. 다음은 main 메소드의 형식을 보여준다.
코드) main 메소드
public classTest {
public static voidmain(String[] args) {
//...
}
}
main 메소드의 접근 제한자는 어떤 외부 클래스에서도 호출할 수 있도록 public 제한자로, 객체 생성 없이 main 메소드를 바로 호출할 수 있도록 static 제한자로 선언해야 한다. 그리고 이 메소드는 어떤 값도 반환하지 않기 때문에 void 로 선언해야 한다. 마지막으로 사용자 입력값을 처리하기 위해 String[] args 가 선언되어야 한다. 이때 args 는 단순한 변수명이기 때문에 어떤 이름으로 선언해도 무방하다.
여기서 여러분들은 public 과 static 제한자가 어떤 것인지 궁금해 할 것이다. 이에 대해서는 차후에 다시 설명하도록 하겠으니 여기서는 main 메소드는 반드시 public static void 로 선언되어야 한다는 것만 기억하도록 하자.
마지막으로 main 메소드를 사용하는 소스 코드를 살펴보도록 하겠다. 이 소스 코드는 사용자로부터 두 개의 문자열을 입력받아 화면에 출력한다.
코드) main 메소드 예제
public classTest {
public static voidmain(String[] args) {
if (args.length != 2) {
System.out.println("두 개의 문자열을 입력해주세요!!");
return;
}
System.out.println("1: " + args[0]);
System.out.println("2: " + args[1]);
}
}
코드) main 메소드 예제 실행 결과
c:\work>javac Test.java
c:\work>java Test easy java
1: easy
2: java
3.7.3 오버로딩(Overloading)
오버로딩(Overloading)이란 한 클래스 내에서 동일한 이름의 메소드를 여러 개 정의하는 것을 말한다. 오버로딩을 통해 메소드의 이름은 동일하면서 매개 변수의 개수나 데이터형이 서로 다른 메소드들을 여러 개 정의할 수 있다. 이렇게 정의된 대표적인 메소드가 우리가 자주 사용하는 println() 메소드이다. 이 메소드는 매개 변수의 데이터형에 상관없이 화면에 해당 값을 출력하는 기능을 가진다. 자바 API 에 정의된 println() 메소드를 살펴보면 다음과 같다.
void println()
void println(boolean x)
void println(char x)
void println(char[] x)
void println(int x)
void println(long x)
void println(float x)
void println(double x)
void println(String x)
void println(Object x)
※ 직접 이 메소드들을 확인하고 싶다면 자바 API 에서 java.lang.System 클래스의 out 변수를 클릭하면 된다.
println() 메소드처럼 오버로딩을 적용하여 메소드를 작성하면 동일한 메소드명으로 다양한 데이터를 받아서 처리할 수 있다. 이 메소드를 사용하는 간단한 예제 코드를 보도록 하자.
class Test {
public static void main(String[] args) {
System.out.println(10);
System.out.println("test");
}
}
오버로딩을 하기 위해서는 다음과 같은 규칙을 반드시 지켜야 한다. 만약 다음과 같은 규칙을 지키지 않고 메소드를 선언하면 메소드가 오버로딩된 것이 아니라 단순히 메소드가 선언된 것에 불과하다.
오버로딩(Overloading) 규칙
1. 메소드는 동일한 이름으로 선언되어야 한다.
2. 매개 변수의 개수나 데이터형이 서로 달라야 한다.
오버로딩 규칙을 적용해 메소드를 선언하면 다음과 같다.
코드) 오버로딩(Overloading) 예제
public classOverloadingTest {
public intadd(int a, int b) {
return a + b;
}
public float add(float a, float b) {
return a + b;
}
public doubleadd(double a, double b, double c) {
return a + b + c;
}
}
OverloadingTest 클래스에는 오버로딩 규칙을 적용해 작성된 3개의 메소드가 선언되어 있다. 모두 같은 이름을 가졌기 때문에 어떤 매개 변수가 들어오는지에 따라 호출되는 메소드가 달라진다. int 형 매개 변수가 지정되면 첫 번째 메소드가 호출되며 float 형 매개 변수가 지정되면 두 번째 메소드가 호출된다. 그런데 만약 a, b 변수 모두 long 형으로 지정되면 어떤 메소드가 호출될까? 이때는 long 형을 표현할 수 있는 float 형 매개 변수를 가진 두 번째 메소드가 호출된다.
3.7.4 오버라이딩(Overriding)
오버라이딩(Overriding)은 부모 클래스에 이미 선언되어 있는 메소드를 하위 클래스에서 재정의하는 것을 말한다. 오버라이딩은 부모 클래스의 형질을 물려받았지만 부모 클래스의 메소드를 그대로 사용하지 않고 특정 기능을 추가하여 사용거나 새로 정의하여 사용하고자 할 때 사용하는 방법이다. 오버로딩과 마찬가지로 오버라이딩을 하기 위해서는 다음과 같은 규칙을 지켜야 한다.
오버라이딩(Overriding) 규칙1. 상위 클래스의 메소드 이름, 리턴형, 매개 변수의 개수, 데이터형이 일치해야 한다.
2. 접근 제한자는 범위가 같거나 더 넓은 접근 제한자로 선언해야 한다.
3. 상위 클래스에서 private, final 접근 제한자로 선언된 메소드는 오버라이딩할 수 없다.
4. 상위 클래스에서 static 제한자로 선언된 메소드를 오버라이딩할 때는 반드시 static 제한자를 선언해야 한다.
오버라이딩 규칙을 적용해 메소드를 선언하면 다음과 같다.
코드) 오버라이딩(Overriding) 예제
public classSuperClass {
public intadd(int a, int b) {
return a + b;
}
public static longadd2(long a, long b) {
return a + b;
}
}
public classSubClass extends SuperClass {
public intadd(int a, int b) {
return a - b;
}
public static longadd2(long a, long b) {
return a - b;
}
}
이 소스 코드에서 SubClass 는 SuperClass 를 상속하고 있다. 그렇기 때문에 SubClass 에서 add() 메소드를 선언하지 않아도 SuperClass 의 add() 메소드를 사용할 수 있다. 하지만 이 코드에서는 상위 클래스의 add() 메소드를 하위 클래스인 SubClass 에서 오버라이딩하고 있다. 그렇기 때문에 하위 클래스의 add() 메소드를 호출하면 더하기 연산이 수행되는 것이 아니라 빼기 연산이 수행된다. 마찬가지로 add2() 메소드도 오버라이딩 되어 있다. 다만 이 메소드는 상위 클래스에 static 으로 선언되어 있기 때문에 하위 클래스에서도 static 제한자로 선언되어 있다.
3.8 가변 매개 변수(Varags)
JDK 5.0 에 추가된 가변 매개 변수(Varargs, 가변 인수라고도 함)는 변하기 쉬운(Variable)과 매개 변수(Arguments)를 합쳐서 만든 용어로서 매개 변수가 변할 수 있다라는 것을 함축하고 있다. 용어 이름에서 알 수 있듯이 매개 변수를 가변적으로 처리할 수 있다는 것은 배열이나 컬렉션을 사용하지 않고도 여러 인수를 메소드에 넘겨줄 수 있다는 것을 의미한다.
[TIP&TECH]
메소드에 선언되어 있는 변수를 매개 변수(Parameter), 이 변수를 통해 실제 넘어오는 값을 인수(Arguments, 인자라고도 함)라고 이 둘을 서로 구분한다. 하지만 이 둘을 구분하지 않고 동일하게 매개 변수라고 해도 된다. 왜냐하면 Parameter 와 Arguments 는 형식 매개 변수냐 실 매개 변수냐의 차이일 뿐, 모두 매개 변수이기 때문이다.
다음 코드를 보자. 다음은 args 매개 변수로 넘어온 값에 따라 화면에 출력하는 변수의 개수를 다르게 하여 화면에 출력하기 위한 코드이다. 이 코드에서 print() 메소드를 여러분이 작성해야 한다면 어떻게 작성하겠는가?
public class Test {
public static void main(String[] args) {
int a = 1;
int b = 2;
int c = 3;
if (args[0].equals("1")) {
print(a);
} else if (args[0].equals("2")) {
print(a, b);
} else if (args[0].equals("3")) {
print(a, b, c);
}
}
}
대부분이 print() 메소드를 다음처럼 작성했을 것이다. 이 방법이 잘못된 것은 아니지만 좀 불편해 보이지 않는가?
public static void print(int a) {
System.out.println(a);
}
public static void print(int a, int b) {
System.out.println(a);
System.out.println(b);
}
public static void print(int a, int b, int c) {
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
이렇게 메소드를 오버로딩(overriding)해서 작성하면 원하는 결과를 얻을 수 있지만 매개 변수의 수가 4개, 5개 이상을 넘어가게 되면 해당 매개 변수의 개수를 처리할 수 있는 메소드도 더 추가되어야 한다. 이러한 불편함을 없앨 수 있는 방법이 바로 가변 매개 변수이다. 가변 매개 변수는 "..."를 사용하여 선언할 수 있으며 이를 사용하면 매개 변수의 수가 얼마나 증가하느냐에 상관 없이 하나의 메소드에서 모두 처리할 수 있다. 위 코드를 가변 매개 변수를 사용해서 변경하면 다음과 같다.
public static void print(int ... vars) {
for (int var : vars) {
System.out.println(var);
}
}
※ 이 코드에서 사용한 for문은 향상된 for문이다. 이에 대해서는 "6장 제어문"에서 살펴보기 바란다.
메소드를 오버로딩하는 것보다 가변 매개 변수를 사용하는 것이 코드를 더 짧고 이해하기 쉽게 한다. 이러한 가변 매개 변수의 구조를 정리하면 다음과 같다.
사용 예)
public static void print(int ... args) {}
private void print(String ... vars) {}
String process(String ... names) {}
3.9 리터럴(Literal)
리터럴(Literal)은 대입문의 오른쪽에 위치하는 값으로서 개발자가 변수에 값을 대입하기 위해 선언하는 값을 말한다. 다음 코드에서 10, 'a', "java"를 리터럴이라고 한다.
코드) 리터럴
int i = 10;
char c = 'a';
String s = "java";
이러한 리터럴은 크게 기본 데이터형과 문자열로 분류할 수 있다.
그림 리터럴(Literal) 종류
3.10 정적 임포트(Static Import)
우리가 일반적으로 클래스를 사용하기 위해 선언하는 임포트(import)와 비슷한 기능을 하는 정적 임포트가 JDK 5.0 에 새로 추가되었다. 이름에서 알 수 있듯이 정적 임포트도 무언가를 프로그램에서 사용하기 위해 선언한다. 먼저 일반적인 임포트를 사용하는 코드를 보도록 하자.
코드)
import java.util.ArrayList;
public class Test {
public static void main(String[] args) {
ArrayList list = new ArrayList();
java.util.Vector vec = new java.util.Vector();
}
}
소스 코드는 java.util 패키지의 ArrayList 클래스와 Vector 클래스를 사용하고 있다. 여기서 ArrayList 클래스는 임포트를 사용하였기 때문에 main() 메소드 내에서는 전체 패키지명을 선언하지 않았다. 하지만 Vector 클래스는 임포트문을 사용하지 않았기 때문에 main() 메소드 내에서 Vector 의 전체 패키지명을 선언하고 있다. 이 코드를 통해서 우리는 임포트는 패키지 명을 사용하지 않고 해당 클래스를 사용할 수 있게 해준다는 것을 알 수 있다. 그러면 정적 임포트는? 아직 정적 임포트가 어떤 기능을 하는지는 살펴보지 않았지만 다음 코드를 보면 정적 임포트가 무엇인지 감이 잡힐 것이다.
코드)
import java.lang.Integer;
import static java.lang.Integer.MAX_VALUE;
public class Test {
public static void main(String[] args) {
int max = MAX_VALUE;
int min = Integer.MIN_VALUE;
}
}
이 코드를 보면 정적 임포트로 Integer 클래스의 정적 변수인 MAX_VALUE 를 임포트하고 있다. 그래서 main() 메소드에서는 이 변수의 클래스명을 지정하지 않고도 사용할 수 있다. 하지만 정적 임포트로 임포트하지 않은 MIN_VALUE 는 해당 클래스명을 선언해야 한다. 이 코드를 통해 여러분은 정적 임포트는 정적 변수를 임포트해서 해당 클래스명을 지정하지 않고도 정적 변수를 사용할 수 있게 한다는 것을 알았을 것이다. 추가적으로 정적 임포트는 정적 변수에만 사용할 수 있는 것은 아니다. static 으로 선언된 정적 메소드에도 사용할 수 있다. 정적 메소드를 임포트하는 것은 정적 변수를 임포트하는 것이랑 동일하다.
코드)
import static java.lang.Math.abs;
public class Test {
public static void main(String[] args) {
int a = abs(-10);
}
}
정리하면, 일반 임포트(import)는 패키지명을 선언하지 않고도 클래스를 사용할 수 있게 하는 것이고 정적 임포트(static import)는 클래스명을 선언하지 않고도 정적 변수와 정적 메소드 같은 정적 멤버(static member)를 사용할 수 있게 하는 것이다.
정적 임포트의 구조를 정리하면 다음과 같다.
정적 임포트를 사용한 간단한 예제 코드를 작성하면 다음과 같다.
import static java.lang.Math.*; //Math 클래스의 모든 메소드 임포트
import static java.lang.Math.max; //Math 클래스의 max 메소드 임포트
import static java.lang.Math.pow; //Math 클래스의 pow 메소드 임포트
3.11 타입 안전 열거형(Typesafe enums)
타입 안전 열거형(Typesafe enums)은 기존의 열거형이 타입에 안전하지 못했던 것을 개선하여 타입에 안전하도록 JDK 5.0 부터 추가한 기능이다. 먼저 상수를 이용한 기존의 열거형을 보도록 하자.
코드)
public class Test {
public static void main(String[] args) {
int selMovie = 1; //고객이 선택한 영화 종류 번호
int loanDay = 0; //대여일
//고객이 선택한 영화가 액션이라면
if (selMovie == Comics.ACTION) {
//대여일은 1일
loanDay = 1;
} else {
//대여일은 3일
loanDay = 3;
}
}
}
//만화 종류 열거형
interface Comics {
public static final int FANTASY = 0;
public static final int ACTION = 1;
public static final int DRAMA = 2;
public static final int SPORTS = 3;
}
//영화 종류 열거형
interface Movie {
public static final int COMEDY = 0;
public static final int DRAMA = 1;
public static final int HORROR = 2;
public static final int ACTION = 3;
}
※ 상수를 이용한 열거형이기 때문에 고객이 선택한 영화 종류 번호를 만화 종류 열거형과 비교해도 에러가 발생하지 않음.
기존의 열거형은 열거형이라고 하기 보다는 단순히 인터페이스에 상수를 선언한 것에 불과하다. 그래서 인터페이스에 선언된 상수들의 의미에 상관 없이 해당 상수들이 갖고 있는 숫자만 일치하면 해당 상수의 의미와 동일한 것으로 프로그램이 판단하는 문제가 있었다. 이를 타입에 안전하지 않다고 하며 이를 개선한 것이 타입 안전 열거형이다. 타입 안전 열거형을 사용하여 위의 코드를 변경해 보도록 하자.
public class Test {
public static void main(String[] args) {
Movie selMovie = Movie.DRAMA; //고객이 선택한 영화 종류 번호
int loanDay = 0; //대여일
//고객이 선택한 영화가 액션이라면
if (selMovie == Movie.ACTION) {
//대여일은 1일
loanDay = 1;
} else {
//대여일은 3일
loanDay = 3;
}
}
}
//만화 종류 타입 안전 열거형
enum Comics {FANTASY, ACTION, DRAMA, SPORTS};
//영화 종류 타입 안전 열거형
enum Movie {COMEDY, DRAMA, HORROR, ACTION};
※ Movie selMovie = Movie.DRAMA : 고객이 선택한 영화 종류도 타입 안전 열거형으로 설정
※ selMovie == Movie.ACTION : 영화 타입 안전 열거형과 비교하지 않을 경우 컴파일 에러 발생
기존의 열거형을 사용하면 열거형의 타입이 다르더라도 정상적으로 컴파일되고 실행되지만 타입 안전 열거형을 사용하면 열거형의 타입을 다르게 사용할 경우 바로 컴파일 에러가 발생한다. 이렇게 컴파일 시간에 오류를 찾아낼 수 있다는 것은 실제 실행 중에는 더 견고하게 동작할 수 있다는 것을 의미한다. 반면에 기존 열거형을 사용할 경우에는 프로그램 실행 중에도 에러가 발생하지 않으므로 오랜 시간이 지난 후에야 프로그램이 이상하게 동작하는 것을 발견할 위험성도 있다.
지금까지 살펴본 타입 안전 열거형의 구조를 정리하면 다음과 같다.
타입 안전 열거형을 사용한 간단한 예제 코드를 작성하면 다음과 같다.
enum Season {SPRING, SUMMER, AUTUMN, WINTER}
enum Car {SONATA, AVANTE, ACCENT, WINSTORM}
3.12 어노테이션(Annotations)
어노테이션은 자바에서 메타데이터를 사용하기 위한 기능이다. 어노테이션은 패키지, 생성자, 메소드, 변수 등에 선언할 수 있는 변경자(modifier)로서 내장(Builtin) 어노테이션을 사용할 수도 있으며 직접 커스텀(Custom) 어노테이션을 작성하여 사용할 수도 있다. 이 장에서는 쉽게 사용해 볼 수 있는 내장 어노테이션에 대해서 살펴보도록 하겠다.
[TIP&TECH]
메타데이터(Metadata)는 데이터에 대한 데이터라는 의미로서 문서화, 컴파일러 체크, 코드 분석 등에 유용하게 사용된다.
어노테이션의 표기법은 문서화 주석을 선언하듯이 어노테이션 이름 앞에 @(at)를 붙이면 된다.
이러한 어노테이션의 용도를 간단히 정리하면 다음과 같다.
1) 컴파일러에게 정보 제공
컴파일 시간에 에러를 검사하거나 경고를 감추게 하기 위해 사용할 수 있다.
2) 컴파일 시간과 디플로이 시간 처리
어노테이션 정보를 이용해 코드, XML 파일 등을 생성할 수 있다.
3) 실행 시간 처리
몇몇 어노테이션은 실행 시간에 검사 목적으로 사용될 수 있다.
3.12.1 내장 어노테이션(Builtin Annotations)의 종류
내장 어노테이션에는 Override 어노테이션, Deprecated 어노테이션, SuppressWarning 어노테이션이 있다. 이 어노테이션들에 대해서 하나씩 살펴보도록 하겠다.
1) Override 어노테이션(@Override)
Override 어노테이션은 메소드에 선언되어 해당 메소드가 상위 클래스의 메소드를 오버라이드 한다는 것을 명시한다. 그렇기 때문에 이 어노테이션이 선언된 메소드가 상위 클래스의 메소드를 오버라이드하지 않는 경우에는 컴파일시 에러가 발생한다. 다음 코드는 @Override 어노테이션을 사용하여 java.lang.Object 객체에 있는 toString() 메소드를 오버라이드하는 코드이다. 하지만 이 코드에서는 toString() 메소드를 tostring() 메소드로 잘못 선언하였기 때문에 컴파일 시간에 오버라이드가 되지 않았다는 에러가 발생한다.
[TIP&TECH]
java.lang.Object 는 모든 클래스의 최상위 클래스로서 명시적으로 상속하지 않아도 자동적으로 상속되는 클래스이다.
코드)
class AnoTest {
@Override
public String tostring() {
return super.toString() + " annotation";
}
}
C:\Test>javac AnoTest.java
Test.java:2: method does not override a method from its superclass
@Override
^
1 error
Override 어노테이션을 선언하지 않았을 경우에는 위와 같이 컴파일 시간에 에러가 발생하지 않는다. 그렇기 때문에 오버라이드해서 java.lang.Object 클래스에 있는 toString() 메소드의 기능이 아닌 AnoTest 클래스만의 toString() 메소드로 처리하고자 했던 기능을 사용할 수 없게 된다. 이는 차후 프로그램 실행 시간에 프로그램이 원하는 데로 동작하지 않는 원인을 찾기 위한 디버깅 시간을 소비해야 한다는 것을 의미한다. 그렇기 때문에 명시적으로 이 메소드는 오버라이드 한다는 것을 어노테이션을 통해 자바 컴파일러에게 알려준다면 메소드 이름을 잘못 선언하여 발생하는 문제를 미연에 방지할 수 있다.
2) Deprecated 어노테이션(@Deprecated)
Deprecated 어노테이션은 더 이상 사용되지 말아야 하는 메소드에 선언한다. 그렇기 때문에 이 어노테이션이 선언된 메소드를 호출할 경우에는 컴파일 시간에 경고가 발생한다.
다음 코드를 보자. 이 코드에서 AnoTest 클래스의 tostring() 메소드에 @Deprecated 어노테이션이 선언됐는데도 불구하고 Test 클래스에서 해당 메소드를 호출하고 있다. 그렇기 때문에 이 코드를 컴파일하면 경고 메시지가 발생한다.
코드)
public class Test {
public static void main(String[] args) {
AnoTest ano = new AnoTest();
System.out.println(ano.tostring());
}
}
class AnoTest {
@Deprecated
public String tostring() {
return super.toString() + " annotation";
}
}
C:\Test>javac -Xlint Test.java
Test.java:6: warning: [deprecation] tostring() in AnoTest has been deprecated
System.out.println(ano.tostring());
^
1 warning
[TIP&TECH]
javac 의 ?Xlint 옵션은 컴파일 시간에 발생한 경고 메시지에 대한 상세 정보를 얻고자 할 때 사용한다. 이 옵션은 ?Xlint 와 같이 단독으로 사용할 수도 있고 ?Xlint:deprecation 나 -Xlint:unchecked 와 같이 얻고자 하는 특정 유형을 지정하여 사용할 수도 있다.
3) SuppressWarnings 어노테이션(@SuppressWarnings)
SuppressWarning 어노테이션은 이름에서 알 수 있듯이 경고를 감추기 위한 어노테이션이다. 이 어노테이션은 클래스나 메소드에 선언할 수 있다.
SuppressWarning 어노테이션은 자바 언어 스펙에 지정된 unchecked 와 deprecation 와 같은 경고문을 감출 수 있다. 다음은 SuppressWarning 어노테이션을 사용하는 다양한 방법을 보여준다.
@SuppressWarnings("unchecked")
@SuppressWarnings("deprecation")
@SuppressWarnings(value={"unchecked"})
@SuppressWarnings(value={"deprecation"})
@SuppressWarnings(value={"unchecked", "deprecation"})
이제 SuppressWarning 어노테이션을 사용하는 다음 예제 코드를 살펴보도록 하자. 다음 코드를 저장하고 컴파일하면 어떻게 될까? unchecked 경고 메시지가 출력된다. 이 경고 메시지가 출력되는 이유는 List 컬렉션에 저장될 타입을 지정하지 않았기 때문이다.
코드)
public class Test {
public static void main(String[] args) {
java.util.List list = new java.util.ArrayList();
list.add("1");
list.add("2");
}
}
C:\Test>javac -Xlint Test.java
Test.java:4: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.List
list.add("1");
^
Test.java:5: warning: [unchecked] unchecked call to add(E) as a member of the raw type java.util.List
list.add("2");
^
2 warnings
이러한 경고 메시지를 보기 싫은 경우에는 SuppressWarning 어노테이션을 클래스나 메소드에 선언하면 된다. 만약 다음과 같이 선언했는데도 불구하고 해당 경고가 사라지지 않는다면 설치된 JDK 의 버전을 확인해야 한다. 왜냐하면 JDK 1.5.0_06 이전 버전에서는 SuppressWarning 어노테이션을 지원하지 않았기 때문이다.
@SuppressWarnings(value={"unchecked"})
//@SuppressWarnings("unchecked") 와 같이 사용할 수도 있음.
public class Test {
public static void main(String[] args) {
java.util.List list = new java.util.ArrayList();
list.add("1");
list.add("2");
}
}
C:\Test>javac -Xlint Test.java
※ ?Xlint 는 unchecked 에 대한 상세 정보를 보기 위한 옵션이다.