7.4 this 메서드와 this()메서드
클래스의 외부에서 멤버(필드, 메서드, 이너 클래스)를 호출하기 위해서는 객체를 먼저 생성한 후 '참조 변수명.멤버명'의 형태로 호출하지만 클래스 내부에서는 개체의 생성 없이 필드와 메서드를 바로 사용할 수 있다고 했다. 하지만 모든 사용할 수 있는 상태의 멤버는 항상 객체 속에서만 존재한다. 그렇다면 어떻게 클래스 내부에서는 객체를 생성하지 않고 바로 필드와 메서드를 사용할 수 있을까?
7.4.1 내부 객체 참조 변수명인 this 키워드
우리는 클래스 내부에서도 객체 안의 멤버를 사용해 왔다. 즉 '참조 변수명. 멤버명'의 형태를 사용해 온 셈이다. 다만 객체를 직접 만들지 않은 것 뿐이다. 모든 메서드에는 자신이 포함된 클래스의 객체를 가리키는 this라는 참조 변수가 있다. 다시 한번 말하지만 모든 멤버는 객체 속에 존재하는 것이므로 우리가 int m = 3이라는 필드를 클래스 내부에서 출력하고자 할 때도 System.out.println(this.m)과 같이 작성해야 한다. 다만 this.를 생략하면 컴파일러가 자동으로 this.를 추가해주기 때문에 지금까지 클래스 내에서 필드와 메서드를 그대로 사용할 수 있었던 것이다. 지역 변수는 멤버가 아니므로 this가 자동으로 붙지 않는다.
다음 예제를 살펴보자. init() 매서드에서 넘겨받은 a,b값을 필드 m과 n의 값에 대입했다. 또한 work()메서드에는 init()메서드를 호출했다. 이렇게 클래스 내부에서 멤버인 필드와 메서드를 호출할 때는 실제로는 this.m, this.n 그리고 this.init()와 같이 표현해야 하며, this.를 생략했을 때 자동으로 추가되는 것이다.
이상의 내용을 다시 한번 정리하면 모든 멤버는 활용할 때 소속과 함께 표기(참조 변수명.멤버명)해야 하며, 클래스 내부에서 멤버를 활용할 때 소속을 표기하지 않으면 컴파일러가 자동으로 소속(this.)를 붙여준다는 것이다.
//클래스 내부에서 필드. 메서드 앞에 붙는 자동으로 붙는 this 키워드
class A{
int m;
int n;
void init(int a, int b) {
int c;
c= 3;
this.m = a; // this.를 생략했을 때 자동으로 추가
this.n = b; // this.를 생략했을 때 자동으로 추가
}
void work(){
this.init(2,3); // this.를 생략했을 때 자동으로 추가
}
}
public class jh {
public static void main(String[] args) {
// 클래스 객체 생성
A a = new A();
// 메서드 호출 / 필드값 활용
a.work();
System.out.println(a.m);
System.out.println(a.n);
}
}
결과
this를 생략해도 항상 컴파일러가 추가해주므로 굳이 신경쓸 필요가 없어 보이지만 그렇지 않다. this.를 명시적으로 붙여 줘야 할 때가 있기 때문이다. 다음 예제를 살펴보자.
class A{
int m;
int n;
void init(int m, int n){
m = m;
n = n; // 필드와 지역변수를 모두 사용할 수 있고 이름이 같을 때 지역 변수로 인식
}
}
필드명은 m,n이며 init(int m, int n) 메서드에도 지역 변수 m과 n이 있다.init() 메서드에서는 입력받은 지역 변수 m과 n에 각각 대입하고자 한다. 먼저 피드와 지역변수의 사용 범위를 알아보자. 필드 m, n은 클래스 내부에 선언돼 있으며, 클래스 전체에서 사용할 수 있다. 반면 init()메서드에서 선언된 지역변수 m,n은 init()메서드 내부에서만 사용할 수 있다. 따라서 init() 메서드 내부에서는 필드 m, n을 사용하면 이는 지역변수일까 필드일까? 지역변수와 필드 모두를 사용할 수 있는 영역에서는 사용 범위가 좁은 변수, 즉 지역 변수로 인식한다. 따라서 init()메서드 안에서 m = m, n = n과 같이 작성하면 컴파일러는 이들 모두를 지역변수로 인식하므로 this.는 당연히 추가되지 않을 것이다. 지역 변수에 지역 변숫값을 다시 대입하는 형태이므로 필드값은 전혀 변화가 없다. 따라서 다음과 같이 객체를 생성한 후 메서드를 호출하고 필드값을 확인하면 모두 값이 0으로 나온다.
A a = new A();
a.init(3, 4);
System.out.prinltn(a.m); //0
System.out.println(a.n); //0
따라서 의도한 바와 같이 넘겨받은 지역 변수 m, n의 값을 필드 m, n에 대입하기 위해서는 다음과 같이 this.m = m, this.n = n과 같이 필드에 this.를 붙여 표기해야 한다.
class B {
int m;
int n;
void init(int m, int n){
this.m = m;
this.n = n;
}
}
B b = new B();
b.init(3, 4);
System.out.println(b.m); //3
System.out.prinltn(b.n); //4
이러한 문제점은 지역 변수와 필드명이 동일하기 때문에 발생한다. 애초에 이름이 서로 달랐다면 this.m = m과 같이 필드와 지역 변수를 명시적으로 구분할 필요가 없겠지만 자바에서 제공하는 대부분의 API에는 메서드의 지역 변수명이 필드명과 동일하게 구성돼 있다. 따라서 this.m = m과 같은 표현은 앞으로도 계속 보게 될 형식이므로 꼭 이해해야 한다.
class A{
int m;
int n;
void init(int m, int n){
m = m;
n = n;
}
}
class B {
int m;
int n;
void init(int m, int n){
this.m = m;
this.n = n;
}
}
public class jh {
public static void main(String[] args) {
// 필드명과 지역 변수명이 같고 this 키워드를 사용하지 않음
A a = new A();
a.init(2, 3);
System.out.println(a.m);
System.out.println(a.n);
// 필드명과 지역 변수명이 같고 this 키워드를 사용함
B b = new B();
b.init(2, 3);
System.out.println(b.m);
System.out.println(b.n);
}
}
결과