Java/Java Study

this & this( ) & super (5월/13일 내용추가)

모모토 2021. 5. 10. 01:57
반응형

● 자바를 배우면서 한 번쯤은 누구나 헷갈릴만한 파트이다. 본 게시물에서 2 부분으로 나눠서 설명을 해보려 한다.

 

1) this & this( )

 

this를 무엇이라고 배웠는지 기억하는가? this는 바로 인스턴스 변수와 지역변수를 구분하기 위해서 쓰는 참조 변수이다.

여기서 인스턴스 변수는 클래스 내에 선언된 변수이고(Static이 붙지 않은), 지역변수는 메서드 내에 정의된 변수이다.

이 둘을 구별한다는 것인데 이게 무슨 의미인지 IPhoneTest라는 클래스를 만들어서 확인해보자

 

class iPhone {
String model;
String color;
int capacity;
	
iPhone(){
	model ="iPhoneX";
	color = "White";
	capacity = 64; 
	}

iPhone (String a , String b , int c) {
	model = a;
	color = b;
	capacity = c;
	}
}


public class IPhoneTest {
	public static void main(String[] args) {
    	iPhone i1 = new iPhone();
	iPhone i2 = new iPhone("iPhone12","SpaceGray",128);
        System.out.println("i1의 model="+i1.model+",color="+i1.color+",capacity="+i1.capacity+"GB");
	System.out.println("i2 model="+i2.model+",color="+i2.color+",capacity="+i2.capacity+"GB");
	}
}

객체 생성 시 i1은 기본 생성자 iPhone()을 거쳐 인스턴스 변수의 초기화가 일어난다.

 

객체 i2는 입력해준 값에 따라 생성자 iPhone (String a , String b , int c)를 거쳐 입력해준 값으로 인스턴스 변수가 초기화가 된다.

 

위의 코드는 this의 개념이 필요 없는 완성된 코드이다. 하지만 만약 소스코드가 복잡해지고 다루어야 할 변수가 현저히 많아진다면 어떻게 할 것인가? Color, Moldel, Capacity, 외에 통신사, 요금제 등등 변수가 많아질 경우 생성자의 매개변수를 a, b, c, d, e, f, g,.... 이런 식으로 반복한다면 프로그램을 다루는 프로그래머 입장에서도 굉장히 난처할 것이다.

 

따라서 매개변수와 인스턴스의 이름을 일치시켜 직관적으로 알 수 있게 해 주는 대신 이것이 인스턴스 변수이고 이것이 지역변수다 라고 표시를 하여 에러를 피하기 위해서 this를 사용한다. this는 해당 클래스의 주소를 담고 있는 참조 변수이다. 따라서 this.model은 class의 인스턴스 변수를 의미한다. 그렇다면 이제 a를 model로 바꾸어도 프로그램은 지역변수와 참조 변수를 정확히 구별하고 에러가 나지 않는다. 그럼 this를 사용해서 바꾸어보자

 

class iPhone {
String model; // this가 가리키는것 1
String color; // this가 가리키는것 2
int capacity; // this가 가리키는것 3
	
iPhone(){
	model ="iPhoneX";
	color = "White";
	capacity = 64; 
	}

iPhone (String model , String color , int capacity) {
	this.model = model;
	this.color = color;
	this.capacity = capacity;
	}
}


public class IPhoneTest {
	public static void main(String[] args) {
    	iPhone i1 = new iPhone();
	iPhone i2 = new iPhone("iPhone12","SpaceGray",128);
        System.out.println("i1의 model="+i1.model+",color="+i1.color+",capacity="+i1.capacity+"GB");
	System.out.println("i2 model="+i2.model+",color="+i2.color+",capacity="+i2.capacity+"GB");
	}
}

this를 사용해 매개변수로부터 입력받은 값을 인스턴스 변수에 넣어준다. 보기엔 별거 아닌 것 같지만 나중에 큰 프로젝트, 프로그램에 쓰일 코드를 다룬다면 굉장히 유용할 것이라고 생각된다.(본인도 큰 프로젝트 안 해봄)

 

간단 정리) this는 인스턴스 변수와 지역변수를 구분하는 역할을 한다. 또한 this를 이용하여 인스턴스 멤버를 호출이 가능하다.


this( )는 this와 비슷한 원리이다. 바로 자기 자신의 생성자를 불러오는 것이다. 바로 코드로 확인해보자

 

class iPhone {
String model;
String color;
int capacity;
	
iPhone(){

    this("iPhoneX","White",64); 
    //iPhone (String model , String color , int capacity) 생성자를 불러옴
	}

iPhone (String model , String color , int capacity) {
	this.model = model;
	this.color = color;
	this.capacity = capacity;
	}
}


public class IPhoneTest {
	public static void main(String[] args) {
    	iPhone i1 = new iPhone();
	iPhone i2 = new iPhone("iPhone12","SpaceGray",128);
        System.out.println("i1의 model="+i1.model+",color="+i1.color+",capacity="+i1.capacity+"GB");
	System.out.println("i2 model="+i2.model+",color="+i2.color+",capacity="+i2.capacity+"GB");
	}
}

this()를 이용하여 생성자 iPhone (String model , String color , int capacity)를 호출해서 기본값으로 초기화하여주는 기본 생성자를 만들었다. 한결 코드가 깔끔해졌다.

 


2) this&this() VS super&super()

 

this와 super의 차이도 굉장히 간단하다. this는 위에서도 말했듯 해당 클래스의 참조 변수로써 지역변수와 인스턴스 변수를 구분해준다. this()는 클래스 안에서 생성자를 호출해준다. SUPER는 자손 클래스에서 조상 클래스의 멤버 변수를 참조하는 참조 변수이다. 조상 클래스로부터 상속을 받은 멤버를 왜 참조하느냐 하는 의문을 자아낼 수 있는데 바로 구분을 하기 위해서이다. 조상 클래스와 자손 클래스 각각에 int 타입의 'x'라는 인스턴스 변수가 중복으로 선언이 되어있을 때, 이 둘을 구분하기 위하여 사용될 수 있다는 것이다. 간단한 예제를 살펴보자

 

public class SuperTest {
	public static void main(String[] args) {
		Child c =new Child();
		c.method();
	}
}

class Parent {
	int x = 10;
}

class Child extends Parent {
	int x = 20;
	void method() {
		System.out.println(x);
		System.out.println(this.x);
		System.out.println(super.x);
	}
}

실행결과

20
20
10 

this는 해당 클래스를 참조하므로 this.x의 결과는 20이 나오게 된다, 반면 super.x 는 조상 타입의 참조 변수로 10을 출력한다. 만약 Child 클래스에 인스턴스 변수 x가 없다면 this는 상속받은 x(=20)으로 접근할 것이다.


super( )는 조상 클래스의 생성자를 호출하는 데 사용된다. 조상 클래스의 생성자를 왜 호출하여야 할까? 

 

자, 자손 클래스가 상속을 받고, 그 자손클래스가 객체로 형성되면, 우리는 자손 클래스의 멤버를 쓸 수 있는 동시에 조상 클래스의 멤버도 쓸 수 있게 되는 것이다. 따라서 언제 쓰일지 모르니 조상과 자손의 멤버 모두 초기화 작업을 해놓아야 한다는 것이다. 이 초기화 작업을 해놓지 않으면 에러가 발생한다. 따라서 이러한 에러를 피하기 위해서 상속관계의 조상 클래스의 멤버를 초기화해줄 자신의 다른 생성자 혹은 조상의 생성자 모양에 맞춰 super() 나 this()를 호출해야 한다. (부연설명 : 대부분의 경우 같은 클래스 내의 생성자는 서로 호출하게 되어있다. 아니면 조상의 생성자 super()를 호출 해야한다. 그래서 this() 또는 super()를 호출해야하는 것이죠, 이건 그냥 규칙입니다. 원래는 조상의 생성자를 호출하면서 상속계층도를 거슬러올라기는데. 같은 클래스의 다른 생성자를 호출하는 경우도 있기때문.) 만약 생성자를 호출해주지 않는다면 컴파일러는 생성자의 첫 줄에 super()를 자동으로 추가한다. 자동으로 super()를 추가할 시에 발생하는 에러가 존재하는데 아래의 코드를 보자.

 

위의 코드는 에러가 발생했다. 바로 조상 클래스의 멤버 초기화를 할 수 없기 때문이다. 왜 할 수 없는가? Point 3D 생성자는 첫 줄에 자신의 다른 생성자가 없기 때문에 자동으로 super();가 삽입되는데 super();는 조상의 Point();이다. 하지만 조상 클래스에는 Point(); 생성자를 찾을수 없기 때문에 에러가 나는것이다. 이를 해결하기 위해선 조상클래스에 기본 생성자 Point();를 추가하거나, Point 3D에 super(x, y);를 호출하여 조상 클래스의 멤버를 초기화해주면 해결된다.

 

public class PointTest {
	public static void main(String[] args) {
		Point3D p3 = new Point3D(1,2,3);
		System.out.println(p3.getLocation());
	}
}

class Point2D {
	int x,y;
	public Point2D() {
		
	}
	Point2D(int x, int y){
		this.x=x;
		this.y=y;
	}
	String getLocation() {
		return "x:" + x + ", y:" + y;
	}
}

class Point3D extends Point2D {
	int z;
	
	Point3D(int x,int y,int z){ // Error!!
		//super(); 컴파일시 삽입
		this.x=x;
		this.y=y;
		this.z=z;
	}
	String getLocation() {
		return "x:" + x + ", y:" + y +", z:" + z;
	}
}