[JAVA]생성자, this, 오버로딩 _ 객체지향

doooly·2023년 7월 4일
0

객체지향

목록 보기
1/1

객체지향 프로그래밍에서는 클래스를 통해 객체를 생성해서 사용하죠.
여기서 객체를 생성할 때 쓰이는 키워드는 'new' 입니다.
또한 객체의 생성하는 과정에서 필드를 초기화하는 메서드를 '생성자'라고 칭하죠.
생성자에 대해 간략하게 알아보겠습니다 🤔🤔


생성자

생성자는 객체를 초기화하기 위해 사용하는 메서드입니다.
생성자의 특징은 다음과 같습니다.

  • 클래스 이름과 이름 같아야 함
  • 메소드지만 return 값이 없음
  • new 뒤에서만 사용 가능

Class A{
	...
    A(){
    	... //기본 생성자
    }
 }
public class Main {
    public static void main(String[] args) {
        A a = new A();
        //new를 통해 생성자 A()를 호출함
    }
}
     

예제에서는 클래스 A가 생성자 A()를 가지고 있음을 알 수 있습니다.
또한 A()는 return 값에 대한 정보를 가지고 있지 않으며 Main함수에서 new 뒤에서 사용되었습니다. 🙂🙂



기본 생성자(디폴트 생성자)

기본 생성자는 위의 예제에 있던 아무 매개변수도 받지 않는 생성자를 일컫습니다.

개발자는 클래스에 임의로 생성자를 여러 개 추가해 사용할 수 있습니다.
그러나 생성자를 만들지 않았을 경우에, main함수에서 new를 사용해 객체를 생성하려고 하면 어떻게 될까요?

Class A{
	...
 }
public class Main {
    public static void main(String[] args) {
        A a = new A();
    }
}

위의 코드에서 클래스 A에는 생성자가 존재하지 않습니다.
그러나 new A()를 통해 생성자에 접근해도 컴파일 오류가 나지 않습니다.

기본 생성자는 ⭐️ 클래스에 생성자가 아예 없다면,⭐️ 컴파일러에 의해 자동으로 생성됩니다.

그렇기 때문에 코드가 없어도 컴파일 오류가 나지 않습니다 !


⚠️ 하지만 새로운 생성자를 만들고 나면, 기본 생성자에 접근할 수 없습니다. (컴파일 에러)

Class A{
	int age;
	A(int age){ ... }
 }
public class Main {
    public static void main(String[] args) {
        A a = new A(); //컴파일에러
        A a2 = new A(11);
    }
}

위의 예제에 int age라는 필드와 age를 매개변수로 받는 생성자를 하나 더 추가했습니다.
클래스에 A(){ ... }가 없으므로, main에서 new A()는 컴파일 오류가 납니다.

코드에 A(){ ... }를 추가하면 오류가 해결됩니다



간단한 예제를 통해 위의 내용을 다시 정리해보겠습니다.


class C1{
    String name;
    int age;
    int height;
    C1(){
        //기본 생성자
        //필드 초기화는 임의로 진행
        name = "Doooly";
        age = 5;
        height = 180;
    }
    C1(String name, int age, int height){
        //매개변수를 받아 필드를 초기화하는 생성자
        this.name = name;
        this.age = age;
        this.height = height;
    }
}
public class Main {
    public static void main(String[] args) {
        C1 obj1 = new C1();
        C1 obj2 = new C1("Obj2", 50, 160);

        System.out.println("<obj1 객체의 필드> [name]: "+ obj1.name+", [age] : "+ obj1.age + ", [height] : "+ obj1.height);
        System.out.println("<obj2 객체의 필드> [name]: "+ obj2.name+", [age] : "+ obj2.age + ", [height] : "+ obj2.height);

    }
}
  • C1 클래스를 만든 후, 필드를 3개 지정했습니다.
  • C1()은 기본 생성자이므로, 생성자를 호출하면 필드가 초기화되도록 임의로 값을 지정했습니다.
  • C1(String name, int age, int height)는 매개변수를 받는 생성자로, 매개변수의 값으로 필드를 초기화했습니다.

따라서 Main 함수에서 C1 객체를 각각의 생성자를 사용해 생성한 후 필드의 값을 출력했을 때, 필드가 올바르게 초기화된 것을 볼 수 있습니다.



생성자는 리턴 값은 없지만 메서드라고 얘기했었죠?
하지만 위의 예제에서 'C1'라는 이름을 가진 메서드 두 개(생성자 2개)를 생성했음에도 오류가 나지 않았습니다.

이를 '오버로딩'이라고 합니다.
함수의 이름은 같지만 매개변수의 개수나 타입을 다르게 정의하는 것을 일컫습니다.
일단 간략하게 오버로딩에 대해 얘기한 후, 후에 다시 언급하겠습니다.



this

생성자는 객체의 필드를 초기화하기 위해 사용한다고 했습니다.
이때 그 객체의 필드를 this 를 통해 가리킵니다.

this -> 객체 자기 자신에 대한 레퍼런스

this는 보통

  • 객체의 멤버 변수와 메소드 변수의 이름이 같은 경우
  • 다른 메소드 호출 시 객체 자신의 레퍼런스 전달
  • 메소드가 객체 자신의 레퍼런스를 반환할 때

사용됩니다.


한 클래스를 가지고 여러 개의 객체를 만들 수 있습니다.

Class A{ 
	String name;
    
    void func(){
    	System.out.println(this.name);
        //객체의 필드를 호출하는 함수
    }
    A(String name){
    //매개변수를 받는 생성자
    	this.name = name;
    }
 }
 public class Main {
    public static void main(String[] args) {
    A a1 = new A("A1");
    A a2 = new A("A2");
    A a3 = new A("A3");
    
    a1.func(); //A1
    a2.func(); //A2
    a3.func(); //A3
 }

a1의 name과 a2의 name, a3의 name은 각각 객체마다 다른 값을 가집니다.

다른 값으로 초기화하기 위해, String type을 매개변수로 받는 생성자를 만들었고, 해당 변수를 객체의 name으로 설정했습니다.
따라서 A a1 = new A("A1");에서 a1 객체의 name은 "A1"으로 초기화됩니다.

이 때 생성자에 사용된 this.는 각 객체를 가리킵니다.

a2 객체를 생성하는 과정에서,
this.name -> a2객체의 String type 필드
name -> 매개변수로 들어온 "A2"





this()

this와 헷갈리는 요소로 this()가 있습니다.

this() -> 클래스 내의 다른 생성자 호출

this()의 특징은 다음과 같습니다.

  • 생성자 내에서만 사용 가능
  • 반드시 생성자 코드 처음에 수행
    • 컴파일러가 첫문장으로 생성자의 속성 파악 위해

class C1{
    String name;
    int age;
    int height;
    C1(){
       this("", 0, 0);
    }
    C1(String name, int age, int height){
        //매개변수를 받아 필드를 초기화하는 생성자
        this.name = name;
        this.age = age;
        this.height = height;
    }
}
public class Q11 {
    public static void main(String[] args) {
        C1 obj1 = new C1();
        C1 obj2 = new C1("Obj2", 50, 160);

        System.out.println("<obj1 객체의 필드> [name]: "+ obj1.name+", [age] : "+ obj1.age + ", [height] : "+ obj1.height);
        System.out.println("<obj2 객체의 필드> [name]: "+ obj2.name+", [age] : "+ obj2.age + ", [height] : "+ obj2.height);

    }
}

위의 예제에서 C1() 생성자를 조금 수정했습니다.

기본생성자 호출 시 필드를 임의로 초기화하지 않고, 다른 생성자를 this()를 통해 호출했습니다.
호출할 생성자가 매개변수를 필요로할 경우, ()안에 매개변수 값을 지정해야 합니다.

this("", 0, 0);문을 사용했으므로 name에 "", age에 0, height에 0이 들어갔음을 알 수 있습니다.





오버로딩

위에서 언급했듯, 오버로딩이란

매개변수의 타입이나 개수를 다르게 해 같은 이름의 메소드를 여러 개 작성

하는 것 입니다.

오버로딩을 이용해 생성자를 여러 개 정의해 필드를 효과적으로 초기화할 수 있었습니다.

class C1{
    String name;
    int age;
    int height;
    
    C1(){ //생성자 1
       this("", 0, 0);
    }
    C1(String name){ //생성자 2
    	this(name, 0, 0);
    }
    C1(int age){ //생성자 3
    	this("", age, 0);
    }
    C1(String name, int age){ //생성자 4
    	this(name, age, 0);
     }
    C1(String name, int height){ //생성자 5
    	this(name, 0, height);
        //오버로딩X
     }
    C1(String name, int age, int height){ //생성자 6
        this.name = name;
        this.age = age;
        this.height = height;
    }
}

3개의 필드가 있을 때, 여러 개의 생성자를 사용해 필드를 초기화하는 모습입니다.

생성자 4는 name과 age를 매개변수로 받고, this()를 통해 생성자 6을 호출해 필드를 초기화합니다. 이 때 name과 age의 값을 생성자6에 전달합니다.

⚠️유의할 점이 있다면, 생성자 5는 오버로딩이 아니라는 점 입니다.

오버로딩은 매개변수의 '개수와 타입'이 달라야 합니다.
그러나 생성자 4는 String, int 타입을 매개변수로 받고 생성자 5도 이와 같습니다. 이는 오버로딩으로 여겨지지 않으므로 오류를 발생시킵니다.




오버로딩은 생성자 외의 메소드에도 사용됩니다.

class Method {
	public int getsum(int i, int j){
		return i+j;
	}
	public double getsum(int i, int j){
		return i+j;
	}
}
//오버로딩 실패
//컴파일러가 무슨 메소드를 호출해야할 지 구분 못함

다음은 i, j를 인자로 받아 둘을 더한 값을 리턴하는 함수입니다.

두 함수의 리턴 타입이 달라 오버로딩으로 생각하기 쉬우나,
매개변수의 타입과 개수가 일치하므로 오버로딩이 아닙니다 !!

0개의 댓글