들어가며
JAVA에서 this와 this()는 비슷하게 생겼지만 엄연히 다르다. 어떠한 차이가 있는지 예제 코드를 통해 이해해보자.
this
생성자의 매개변수의 ≠ 이름 인스턴스 변수의 이름
class Car {
String color;
String gearType;
int door;
Car(String a, String b, int c){
color = a;
gearType = b;
door = c;
}
public static void main(String[] args) {
Car c = new Car("black", "auto", 3);
System.out.println(c.color);
System.out.println(c.gearType);
System.out.println(c.door);
}
}
this 키워드는 인스턴스 자신을 가리키는 참조변수로, 인스턴스의 주소가 저장되어 있다.
또한 생성자의 매개변수로 선언된 변수의 이름이 인스턴스 변수의 이름과 같을 때 인스턴스 변수와 지역변수를 구분하기 위해서 사용한다는 점을 숙지하고 코드를 살펴보자.
위의 Car 클래스의 인스턴스 변수는 color, gearType, door이고 생성자의 매개변수는 a, b, c 이다. 즉 "생성자의 매개변수의 이름 ≠ 인스턴스 변수의 이름" 이기 때문에 this 키워드를 사용할 필요가 없다.
black
auto
3
출력 결과는 다음과 같다
생성자의 매개변수의 = 이름 인스턴스 변수의 이름 (this 미사용)
class Car {
String color;
String gearType;
int door;
Car(String color, String gearType, int door){
color = color;
gearType = gearType;
door = door;
}
public static void main(String[] args) {
Car c = new Car("black", "auto", 3);
System.out.println(c.color);
System.out.println(c.gearType);
System.out.println(c.door);
}
}
위의 Car 클래스의 인스턴스 변수는 color, gearType, door이고 생성자의 매개변수는 color, gearType, door이다. 즉 "생성자의 매개변수의 이름 = 인스턴스 변수의 이름" 이기 때문에 this 키워드를 사용해야 한다.
하지만 위의 코드에서는 사용하지 않았고, 실제로 IDE에 작성해보면 컴파일 오류도 없다. 그러면 단지 this는 가독성만을 위한 것일까?
null
null
0
출력결과를 보면 예상 결과와 다르다. black, auto, 3 으로 출력이 될 줄 알았지만 null, null, 0으로 추력이 되었다. 왜 그런 것일까?
Java에서 클래스의 인스턴스 변수와 생성자의 매개변수 이름이 같을 때, 매개변수가 지역 변수로서의 역할을 하게 되어 해당 스코프(여기서는 생성자의 스코프) 내에서 우선권을 가진다.
결과적으로, 생성자 또는 메서드 내에서 매개변수 이름만을 사용하면 해당 매개변수를 가리키게 되고 인스턴스 변수가 아닌, 각 매개변수가 자기 자신의 값을 다시 자신에게 할당하게 된다.
자바에서 클래스의 인스턴스 변수는 명시적으로 초기화되지 않았을 때, 각 타입의 기본값으로 초기화 되므로 color(String), gearType(String), door(int) 인스턴스 변수는 순서대로 기본값인 null, null, 0으로 초기화 되는 것이다.
생성자의 매개변수의 = 이름 인스턴스 변수의 이름 (this 사용)
class Car {
String color;
String gearType;
int door;
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
public static void main(String[] args) {
Car c = new Car("black", "auto", 3);
System.out.println(c.color);
System.out.println(c.gearType);
System.out.println(c.door);
}
}
this 키워드를 사용하여 인스턴스 변수와 매개변수를 구분하였다.
인스턴스 변수와 생성자의 매개변수가 이름이 같아도 생성자에서 받은 매개변수의 값을 인스턴스 변수에 올바르게 할당할 수 있게되었다.
black
auto
3
예상대로 출력되는 것을 볼 수 있다.
※ 참고
1. 'this'를 사용할 수 있는 것은 인스턴스 멤버뿐이다.
2. 앞에서도 강조했듯이 'this'는 참조변수로 인스턴스 자신을 가리킨다. 즉 참조변수를 통해 인스턴스의 멤버(멤버 변수, 멤버 함수)에 접근할 수 있는 것처럼, 'this'로 인스턴스 변수에 접근할 수 있는 것이다.
3. 하지만 static 메서드(클래스 메서드)에서는 인스턴스 멤버들을 사용할 수 없는 것처럼, 'this' 역시 사용할 수 없다.
왜냐하면, static 메서드는 인스턴스를 생성하지 않고도 호출이 될 수 있기 때문에, static 메서드가 호출된 시점에 인스턴스가 존재하지 않을 수도 있기 때문이다.
4. 생성자를 포함한 모든 인스턴스 메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 'this'가 지역변수로 숨겨진 채로 존재한다.
this()
class Car{
String color;
String gearType;
int door;
Car(){
this("white", "auto", 4);
}
Car(String color){
this(color, "auto", 4);
}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car("black", "auto", 3);
Car c3 = new Car("white");
System.out.println(c1.color);
System.out.println(c1.gearType);
System.out.println(c1.door);
System.out.println(c2.color);
System.out.println(c2.gearType);
System.out.println(c2.door);
System.out.println(c3.color);
System.out.println(c3.gearType);
System.out.println(c3.door);
}
}
this(), this(매개변수)는 생성자로, 같은 클래스의 다른 생성자를 호출할 때 사용한다.
주의할 점은 총 두 가지이다.
첫번째, 생성자의 이름으로 클래스 이름 대신 this를 사용해야 한다.
두번째, 한 생성자에서 다른 생성자를 호출할 때에는 반드시 첫 줄에서만 호출이 가능하다.
출력 결과에 대해서는 좀 더 밑에서 설명하겠다.
잘못된 this()의 사용
Car(String color) {
door = 5;
Car(color, "auto", 4);
}
먼저 잘못된 예시 코드를 보자. 두 가지 문제점이 보이는가?
첫번째 문제점은 생성자의 두 번째 줄에서 다른 생성자를 호출했다는 점이고, 두번째 문제점은 다른 생성자를 호출할 때 this()를 이용하지 않았다는 점이다.
올바른 this()의 사용
Car(String color) {
this(color, "auto", 4);
}
이것이 올바른 코드이다. 생성자에서 다른 생성자를 호출할 때 첫 줄에서 호출했으며, 클래스 이름 대신 this를 사용하였다는 점이 핵심이다.
출력
class Car{
String color;
String gearType;
int door;
Car(){
this("white", "auto", 4);
}
Car(String color){
this(color, "auto", 4);
}
Car(String color, String gearType, int door){
this.color = color;
this.gearType = gearType;
this.door = door;
}
public static void main(String[] args) {
Car c1 = new Car();
Car c2 = new Car("black", "auto", 3);
Car c3 = new Car("white");
System.out.println(c1.color);
System.out.println(c1.gearType);
System.out.println(c1.door);
System.out.println(c2.color);
System.out.println(c2.gearType);
System.out.println(c2.door);
System.out.println(c3.color);
System.out.println(c3.gearType);
System.out.println(c3.door);
}
}
개념을 이해했으니, 출력을 예상해보자.
Car c1 = new Car();
c1은 매개변수가 없는, 기본 생성자로 생성한 인스턴스의 참조변수이다.
Car() 내부를 살펴보면 this.("white", "auto", 4)를 호출하고 있는데, 이는 Car(String color, String gearType, int door)를 호출한 것과 마찬가지이다. 따라서 System.out.println()으로 출력한 c1.color, c1.gearType, c1.door은 순서대로 "white", "auto", 4가 된다 .
Car c2 = new Car("black", "auto", 3);
c2는 매개변수가 있는 생성자로 생성한 인스턴스의 참조변수이다.
Car(String color, String gearType, int door) 내부를 살펴보면 인스턴스 멤버 변수들을 생성자의 매개변수들로 초기화해주고 있다. 따라서 System.out.println()으로 출력한 c2.color, c2.gearType, c2.door은 순서대로 "black", "auto", 3가 된다.
Car c3 = new Car("white");
c3는 매개변수가 있는 생성자로 생성한 인스턴스의 참조변수이다.
Car c2 = new Car("black", "auto", 3)와 다른점은 매개변수의 개수가 다르다는 점이다. 이를 생성자 오버로딩이라고 하는데, 메소드와 마찬가지로 생성자도 여러 개 선언가능하다. Car(String color); 내부를 살펴보면 this(color, "auto", 4)를 호출하고 있는데, 이는 Car(String color, String gearType, int door)를 호출한 것과 마찬가지이다. 따라서 color= "white"가 전달 되어서, System.out.println()으로 출력한 c3.color, c3.gearType, c3.door은 순서대로 "white", "auto", 4가 된다.
※ 참고
생성자에서 다른 생성자를 첫 줄에서만 호출이가능하도록 한 이유
- 생성자 내에서 초기화 작업 도중에 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기 때문이다.
정리
1. JAVA에서 this는 인스턴스 자신을 가리키는 참조변수, this()는 같은 클래스의 다른 생성자를 호출할 때 사용하는 생성자이다.
2. 한 생성자에서 다른 생성자를 호출할 때 생성자의 이름으로 클래스이름 대신 this()를 사용해야 하고, 반드시 첫 줄에서만 호출이 가능하다.
참고
Java의 정석 | 남궁성 - 교보문고
Java의 정석 | 자바의 기초부터 실전활용까지 모두 담다!자바의 기초부터 객제지향개념을 넘어 실전활용까지 수록한『Java의 정석』. 저자의 오랜 실무경험과 강의한 내용으로 구성되어 자바를
product.kyobobook.co.kr
'JAVA' 카테고리의 다른 글
[JAVA] Generic 타입 (0) | 2024.04.10 |
---|---|
[JAVA] 문자열 비교 방법의 차이 (==, equals, matches) (1) | 2024.02.25 |