Home > Archive > Java_archive > ☕️[Java] 생성자 - 도입

☕️[Java] 생성자 - 도입
Java Programming Language

생성자 - 도입

  • 프로그래밍을 하다보면 객체를 생성하고 이후에 바로 초기값을 할당해야 하는 경우가 많습니다.
    • 따라서 앞서 initMember(...)와 같은 메서드를 매번 만들어야 합니다.
      • “그래서 대부분의 객체 지향 언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 생성자라는 기능을 제공합니다.”
        • 생성자를 사용하면 객체를 생성하는 시점에 즉시 필요한 기능을 수행할 수 있습니다.

생성자는 앞서 살펴본 initMember(...)와 같이 메서드와 유사하지만 몇가지 다른 특징이 있습니다.

아래 코드를 보면서 이해해봅시다.

MemberConstruct

package construct;

public class MemberConstruct {
  String name;
  int age;
  int grade;

  MemberConstruct(String name, int age, int grade) {
    System.out.println("생성자 호출 name=" + name + ",age=" + age + ",grade=" + grade);
    this.name = name;
    this.age = age;
    this.grade = grade;
  }
}

“다음 부분이 바로 생성자입니다.”

MemberConstruct(String name, int age, int grade) {
    System.out.println("생성자 호출 name=" + name + ",age=" + age + ",grade=" + grade);
    this.name = name;
    this.age = age;
    this.grade = grade;
}

“생성자는 메서드와 비슷하지만 다음과 같은 차이가 있습니다.”

  • 생성자의 이름은 클래스 이름과 같아야 합니다. 따라서 첫 글자도 대문자로 시작합니다.
  • 생성자는 반환 타입이 없습니다. 비워두워야 합니다.
  • 나머지는 메서드와 같습니다.

ConstructMain1

package construct;

public class ConstructMain1 {

  public static void main(String[] args) {
    MemberConstruct member1 = new MemberConstruct("user1", 15, 90);
    MemberConstruct member2 = new MemberConstruct("user2", 16, 80);

    MemberConstruct[] members = { member1, member2 };

    for (MemberConstruct member : members) {
      System.out.println("이름:" + member.name + " 나이:" + member.age + "성적:" + member.grade);
    }
  }
}

실행 결과

생성자 호출 name=user1,age=15,grade=90
생성자 호출 name=user2,age=16,grade=80
이름:user1 나이:15성적:90
이름:user2 나이:16성적:80

생성자 호출

  • 생성자는 인스턴스를 생성하고 나서 즉시 호출됩니다.
    • 생성자를 호출하는 방법은 다음 코드와 같이 new 명령어 다음에 생성자 이름과 매개변수에 맞추어 인수를 전달하면 됩니다.
      new 생성자이름(생성자에 맞는 인수 목록)
      new 클래스이름(생성자에 맞는 인수 목록)
      

참고로 생성자 이름이 클래스 이름이기 때문에 둘다 맞는 표현입니다.

new MemberConstruct("user1", 15, 90)
  • 이렇게 하면 인스턴스를 생성하고 즉시 해당 생성자를 호출합니다.
    • 여기서는 Memeber 인스턴스를 생성하고 바로 MemberConstruct(String name, int age, int grade) 생성자를 호출합니다.

참고로 new 키워드를 사용해서 객체를 생성할 때 마지막에 괄호()도 포함해야 하는 이유가 바로 생성자 때문입니다. 객체를 생성하면서 동시에 생성자를 호출한다는 의미를 포함합니다.

생성자 장점

중복 호출 제거
생성자가 없던 시절에는 생성 직후에 어떤 작업을 수행하기 위해 다음과 같이 메서드를 직접 한번 더 호출해야 했습니다.
“생성자 덕분에 객체를 생성하면서 동시에 생성 직후에 필요한 작업을 한번에 처리할 수 있게 되었습니다.”

// 생성자 등장 전
MemberInit member = new MemberInit();
member.initMember("user1", 15, 90);

// 생성자 등장 후
MemberConstruct member = new MemberConstruct("user1", 15, 90);

제약 - 생성자 호출 필수
위 코드에서 생성자 등장 전 코드를 보며 이해해봅시다.
“이 경우 initMember(...)를 실수로 호출하지 않으면 어떻게 될까요?”

  • 이 메서드를 실수로 호출하지 않아도 프로그램은 작동합니다.
    • 하지만 학생의 이름과 나이, 성적 데이터가 없는 상태로 프로그램이 작동하게 됩니다.

“만약에 이 값들을 필수로 반드시 입력해야 한다면, 시스템에 큰 문제가 발생할 수 있습니다.”

  • 결국 아무 정보가 없는 유령 학생이 시스템 내부에 등장하게 됩니다.

생성자의 진짜 장점은 객체를 생성할 때 직접 정의한 생성자가 있다면 “직접 정의한 생성자를 반드시 호출” 해야 한다는 점입니다.

참고로 생성자를 메서드 오버로딩 처럼 여러개 정의할 수 있는데, 이 경우에는 하나만 호출하면 됩니다.

MemberConstruct 클래스의 경우 다음 생성자를 직접 정의했기 때문에 직접 정의한 생성자를 반드시 호출해야 합니다.”

MemberConstruct(String name, int age, int grade) { ... }

다음과 같이 직접 정의한 생성자를 호출하지 않으면 컴파일 오류가 발생합니다.

MemberConstruct member3 = new MemberConstruct(); // 컴파일 오류 발생
member3.name = "user1";

컴파일 오류 메시지

java: constructor MemberConstruct in class construct.MemberConstruct cannot be applied to given types;
  required: java.lang.String,int,int
  found:    no arguments
  reason: actual and formal argument lists differ in length

컴파일 오류는 IDE에서 즉시 확인할 수 있는 좋은 오류입니다.

  • 이 경우 개발자는 객체를 생성할 때, 직접 정의한 생성자를 필수로 호출해야 한다는 것을 바로 알 수 있습니다.
    • 그래서 필요한 생성자를 찾아서 다음과 같이 호출할 것입니다.
      MemberConstruct member = new MemberConstruct("user1", 15, 90);
      

“생성자 덕분에 학생의 이름, 나이, 성적은 항상 필수로 입력하게 됩니다.”

  • 따라서 아무 정보가 없는 유령 회원이 시스템 내부에 등장할 가능성을 원천 차단합니다.

“생성자를 사용하면 필수값 입력을 보장할 수 있습니다.”

참고: 좋은 프로그램은 무한한 자유도가 주어지는 프로그램이 아니라 적절한 제약이 있는 프로그램입니다.