생성자 - 필요한 이유
객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)을 이용하면 된다.
public class MemberInit {
String name;
int age;
int grade;
}
public class MethodInitMain1 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
member1.name = "user1";
member1.age = 15;
member1.grade = 90;
MemberInit member2 = new MemberInit();
member2.name = "user2";
member2.age = 16;
member2.grade = 80;
MemberInit[] members = {member1, member2};
for (MemberInit s : members) {
System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
}
}
}
회원 객체를 생성하고 나면 `name`, `age`, `grade` 같은 변수에 초기값을 설정한다. 회원 객체를 제대로 사용하기 위해서는 객체를 생성하자마자 이런 초기값을 설정해야 한다. 이 코드에서는 회원의 초기값을 설정하는 부분이 계속 반복된다.
public class MethodInitMain2 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
initMember(member1, "user1", 15, 90);
MemberInit member2 = new MemberInit();
initMember(member2, "user2", 16, 80);
MemberInit[] members = {member1, member2};
for (MemberInit s : members) {
System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
}
}
static void initMember(MemberInit member, String name, int age, int grade) {
member.name = name;
member.age = age;
member.grade = grade;
}
}
`initMember(...)` 메서드를 사용해서 반복을 제거했다. 그런데 이 메서드는 대부분 `MemberInit` 객체의 변수를 사용한다. 이런 경우 속성과 기능을 한 곳에 두는 것이 더 나은 방법이다. 쉽게 이야기해서 `MemberInit`이 자기 자신의 데이터를 변경하는 기능(메서드)을 제공하는 것이 좋다.
this
public class MemberInit {
String name;
int age;
int grade;
//추가
void initMember(String name, int age, int grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}
public class MethodInitMain3 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
member1.initMember("user1", 15, 90);
MemberInit member2 = new MemberInit();
member2.initMember("user2", 16, 80);
MemberInit[] members = {member1, member2};
for (MemberInit s : members) {
System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
}
}
}
`initMember(String name...)`의 코드를 보면 메서드의 매개변수에 정의한 `String name`과 `Member`의 멤버 변수 이름이 `String name`으로 둘 다 똑같다. 나머지 변수 이름도 `name`, `age`, `grade`로 모두 똑같다.
- 이 경우 멤버 변수보다 매개변수가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선 순위를 가진다. 따라서 `initMember(String name, ... )` 메서드 안에서 `name`이라고 적으면 매개변수에 접근하게 된다.
- 멤버 변수에 접근하려면 앞에 `this.`이라고 해주면 된다. 여기서 `this`는 인스턴스 자신의 참조값을 가리킨다.
this의 생략
`this`는 생략할 수 있다. 이 경우 변수를 찾을 때 가까운 지역변수(매개변수도 지역변수다)를 먼저 찾고 없으면 그 다음으로 멤버 변수를 찾는다. 멤버 변수도 없으면 오류가 발생한다.
public class MemberThis {
String nameField;
void initMember(String nameParameter) {
nameField = nameParameter;
}
}
- `nameField`는 먼저 지역변수(매개변수)에서 같은 이름이 있는지 찾는다. 이 경우 없으므로 멤버 변수에서 찾는다.
- `nameParameter`는 먼저 지역변수(매개변수)에서 같은 이름이 있는지 찾는다. 이 경우 매개변수가 있으므로 매개변수를 사용한다.
생성자 - 도입
프로그래밍을 하다보면 객체를 생성하고 이후에 바로 초기값을 할당해야 하는 경우가 많다. 그래서 대부분의 객체 지향 언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 생성자라는 기능을 제공한다.
생성자를 사용하면 객체를 생성하는 시점에서 즉시 필요한 기능을 수행할 수 있다.
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;
}
생성자는 메서드와 비슷하지만 다음과 같은 차이가 있다.
- 생성자의 이름은 클래스 이름과 같아야 한다. 따라서 첫 글자도 대문자로 시작한다.
- 생성자는 반환 타입이 없다.
- 나머지는 메서드와 같다.
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 s : members) {
System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
}
}
}
생성자 호출
생성자는 인스턴스를 생성하고 나서 즉시 호출된다. 생성자를 호출하는 방법은 `new` 명령어 다음에 생성자 이름과 매개변수에 맞추어 인수를 전달하면 된다.
new 생성자이름(생성자에 맞는 인수 목록)
new 클래스이름(생성자에 맞는 인수 목록)
생성자 이름은 클래스 이름이기 때문에 둘 다 맞는 표현이다.
`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";
no suitable constructor found for MemberConstruct(no arguments) // 오류 메시지
생성자는 사용하면 필수값 입력을 보장할 수 있다.
기본 생성자
이전 코드를 보면 생성자를 만들지 않았는데 생성자를 호출한 적이 있다.
public class MethodInitMain1 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
...
}
}
여기서 `new MemberInit()` 이 부분은 분명히 매개변수가 없는 다음과 같은 생성자가 필요할 것이다.
public class MemberInit {
String name;
int age;
int grade;
MemberInit() { //생성자 필요
}
}
기본 생성자
- 매개변수가 없는 생성자를 기본 생성자라 한다.
- 클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고, 작동하는 코드가 없는 기본 생성자를 자동으로 만들어준다.
- 자바가 자동으로 생성해주는 기본 생성자는 클래스와 같은 접근 제어자를 가진다.
- 생성자가 하나라도 있으면 자바는 기본 생성자를 만들지 않는다.
`MemberInit` 클래스의 경우 생성자를 만들지 않았으므로 자바가 자동으로 기본 생성자를 만들어 준 것이다.
생성자 - 오버로딩과 this()
생성자도 메서드 오버로딩처럼 매개변수만 다르게 해서 여러 생성자를 제공할 수 있다.
public class MemberConstruct {
String name;
int age;
int grade;
// 추가
MemberConstruct(String name, int age) {
this.name = name;
this.age = age;
this.grade = 50;
}
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;
}
}
새로 추가한 생성자는 `grade`를 받지 않는다. 대신에 `grade`는 `50`점이 된다.
생성자를 오버로딩 한 덕분에 성적 입력이 꼭 필요한 경우에는 `grade`가 있는 생성자를 호출하면 되고, 그렇지 않은 경우에는 `grade`가 없는 생성자를 호출하면 된다. `grade`가 없는 생성자를 호출하면 성적은 `50`점이 된다.
this()
두 생성자를 비교해 보면 코드가 중복되는 부분이 있다.
this.name = name;
this.age = age;
이때 `this()` 라는 기능을 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있다. 참고로 `this`는 인스턴스 자신의 참조값을 가리킨다. 그래서 자신의 생성자를 호출한다고 생각하면 된다.
public class MemberConstruct {
String name;
int age;
int grade;
MemberConstruct(String name, int age) {
this(name, age, 50); //변경
}
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;
}
}
`this()`는 생성자 코드의 첫 줄에만 작성할 수 있다.
'Java' 카테고리의 다른 글
Google Java Style Guide (2) | 2024.10.25 |
---|---|
[Java/김영한] 패키지 (1) | 2024.09.08 |
[Java/김영한] 객체 지향 프로그래밍 (0) | 2024.09.07 |
[Java/김영한] 기본형과 참조형 (1) | 2024.09.07 |
[Java/김영한] 클래스와 데이터 (0) | 2024.09.06 |