JAVA의 정석

(객체part) 5. 오버로딩과 생성자 / this() vs this.

499.token.required 2023. 8. 10. 19:21

메서드도 변수와 같이 클래스 내에서 서로 구별될 수 있어야 하기 때문에 각기 다른 이름을 가져야 하지만

 

자바에서는 한 클래스 내에 이미 사용하려는 이름과 같은 메서드가 있더라도

 

매개 변수의 개수 또는 타입이 다르다면 같은 이름을 사용해서 정의할 수 있다.  (단 , 반환타입은 아무런 영향을 주지 못한다.)

 

이렇게 한 클래스 내에 같은 이름의 메서드를 여러 개 정의하는 것을 '오버로딩'이라 한다.

 

 

오버로딩이 아닌 중복 정의의 예 1) 

static int add ( int a, int b ) {

return a+b;

}

static int add ( int x, int y ) {

return x+y;

}

 

위의 두 메서드는 매개 변수의 이름만 다를 뿐 , 매개 변수의 타입과 개수가 같다. 그렇기 때문에 오버로딩이 성립되지 않고

 

중복 정의에 불구하다. 실제로 이클립스에 실행해보면 컴파일 오류가 뜬다는 것을 알 수 있다. 

 

오버로딩이 아닌 중복 정의의 예 2)

 

static int add ( int a, int b ) {

return a+b;

}

static long add ( int x, int y ) {

return (long)(x+y) ;

}

 

이번 경우는 리턴타입만 다른 경우이다. 매개변수의 타입과 개수가 여전히 일치하여 오류이고, 반환타입만 바꿔 주어도 컴파일 오류가

 

고쳐지지 않는 것을 통해, 반환 타입은 아무런 영향을 주지 못한다는 것을 알 수 있다. 


 

오버로딩의 옳은 예 

static int add ( int a, int b ) {

return a+b;

}

static long add ( long a, int b ) {

return (long)(a+b) ;

 

 

매개 변수의 개수는 같지만 파라미터 a의 타입이 다르기 때문에 이는 오버로딩이 성립된다.

 

이 경우에는 호출 시 매개변수 값에 의해 호출될 메서드가 구분될 수 있으므로 중복된 메서드가 아닌 것이다.

 

예를 들어 파라미터 a와 b 각각 int type으로 호출 시 첫 번째 메서드가 호출 될 것이고, a에는 long type b에는 int type으로 호출 시

 

두 번째 메서드가 호출 될 것이다. 

 

 

static long add ( int a, long b ) {

return (long) (a + b);

}

static long add ( long a, int b ) {

return (long)(a+b) ;

}

 

 

이 경우 역시 위 경우와 같은 맥락이다. 하지만 주의해야 할 점이 있다. 

 

add(3,3)과 같이 호출 시 두 메서드 중 어느 메서드가 호출된 것인지 알 수 없기 때문에 메서드를 호출하는 곳에서 컴파일에러가 발생한다.

 

메서드 실행 순서

 

 

실행 결과를 add메서드가 println메서드 보다 먼저 호출되는지 의아할 수 있다.

 

 

하지만 호출 스택을 이해하면 쉽다. 호출 스택 맨 밑에 메인 메서드가 있다고 생각하고 그 위에 println , 그 위에 add메서드가 있다고 

 

그림을 그려 생각해보자 .

 

우선 println 메서드를 수행하려면 괄호 안의 문자가 완성되어야 한다. 그렇기 때문에 mm.add가 먼저 호출되고 그 다음 println이 

 

호출된 것이다.

 

 

int result = mm.add(3,3);

System.out.println("mm.add(3,3) 결과 : " + result);

 

위 문장이 이해가 안된다면 풀어서 아래의 두 문장을 하나로 합친 것이라고 생각하면 이해가 쉬워진다. 


 

생성자 (constructor) 

 

생성자는 인스턴스가 생성될 때 호출되는 '인스턴스 초기화 메서드'이다. 따라서 인스턴스 변수 초기화 작업에 주로 사용되며,

 

인스턴스 생성시에 실행되어야 하는 작업을 위해 사용된다. 

 

생성자의 이름은 클래스의 이름과 같아야 하며 리턴 값이 없어야 한다. 

 

생성자 역시, 오버로딩이 가능하므로 하나의 클래스에 여러 개의 생성자가 존재할 수 있다. 

 

연산자 new가 인스턴스를 생성하는 것이지, 생성자가 인스턴스를 생성하는 것이 아니다.

 

사실 모든 클래스에는 반드시 하나 이상의 생성자가 정의되어 있어야 한다 . 하지만 지금까지 생성자를 정의않고도 인스턴스를 생성할 수 

 

있었던 것은 컴파일러가 제공하는 기본 생성자 덕분이었다. 기본 생성자는 클래스의 하나의 생성자도 없을 때만 자동으로 생성된다.

 

생성자를 생성하는 법은 다음과 같다.

 

 

만약 클래스의 접근 제어자가 public 인 경우 기본 생성자로 public 클래스이름(){}이 추가된다.

 

위 코드는 매개변수가 있는 생성자와 기본 생성자 각각의 방법으로 객체를 생성하였다.

 

양쪽 코드 모두 같은 내용이지만, 매개 변수를 이용한 객체 생성이 더 간결하고 직관적인 것을 알 수 있다.

 

생성자에서 다른 생성자 호출하기 - this ()

 

같은 클래스의 멤버들 간에 서로 호출할 수 있는 것처럼 생성자 간에도 서로 호출이 가능하다. 단 다음의 두 조건을 만족시켜야한다.

 

- 생성자의 이름으로 클래스이름 대신 this를 사용한다.

- 한 생성자에서 다른 생성자를 호출할 때는 반드시 첫줄에서만 호출이 가능하다.

 

생성자 내에서 다른 생성자를 호출한 예는 다음과 같다. 

 

만약 생성자 내에서 다른 생성자를 호출할 때,

 

public Person() {

Person("saint",13);

}

 

이런 식으로 호출 하게 되면 this()가 아니기 때문에 컴파일 오류를 일으킬 것이고, 

 

public Person() {

int j;

this("saint",13);

}

 

이런 식으로 호출 하게 되면 첫 줄이 아니어서 컴파일 오류가 일어난다.

 

생성자에서 다른 생성자를 첫 줄에서만 호출이 가능하도록 한 이유는 생성자 내에서 초기화 작업 도중에 다른 생성자를 호출하게 되면,

 

호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 초기화 작업이 무의미해질 수 있기

 

때문이다. 

 

객체 자신을 가르키는 참조변수 - this

 

Car ( String c, String g, int d ){

 

color = c ;

gearType = g ;

door = d;

 

}

 

color = c는 생성자의 매개변수로 선언된 지역변수 c의 값을 인스턴스 변수 color에 저장한다. 이 때 변수 color와 c는 이름만으로도

 

서로 구별되므로 아무런 문제가 없다. 하지만 ,

 

 

Car ( String color, String gearType, int door ){

 

this.color = color ;

this.gearType = gearType ;

this.door = door ;

 

}

 

 

 

위와 같이 , 매개변수로 선언된 변수의 이름이 인스턴스 변수 color와 같을 경우에는 이름만으로는 두 변수가 서로 구별이 안된다.

 

이런 경우에는 앞에 'this'를 사용하면 된다.  이렇게 하면 this.color는 인스턴스 변수이고, color는 생성자의 매개변수로 정의된

 

지역변수로 서로 구별이 가능하다. 만일 오른쪽 코드에서 'this.color= color' 대신 'color = color' 와 같이 하면 둘 다 지역변수로 간주된다. ( 지역 변수가 더 가깝게 선언되어 있기 때문에) 

 

'this' 는 참조 변수로 인스턴스 자신을 가르킨다. 참조변수를 통해 인스턴스의 멤버에 접근할 수 있는 것 처럼 , 'this'로 인스턴스 변수에 접근할 수 있는 것이다.

 

하지만 'this' 를 사용할 수 있는 것은 인스턴스 멤버 뿐이다. (인스턴스 변수 x, 인스턴스 멤버 o (메서드, 변수 등 ) )

 

static 메서드에서는 인스턴스 멤버들을 사용할 수 없는 것처럼,  'this'역시 사용할 수 없다.

 

왜냐하면 , static 메서드는 인스턴스를 생성하지 않고도 호출될 수 있으므로  static메서드가 호출된 시점에 인스턴스가 존재하지 않을수도 있기 때문이다. 

 

사실 생성자를 포함한 모든 인스턴스메서드에는 자신이 관련된 인스턴스를 가리키는 참조변수 'this가' 지역변수로 숨겨진 채로 존재한다.