생성자란
작성한 클래스를 기반으로 객체의 생성을 위해 생성자를 사용합니다. 코틀린의 클래스는 주 생성자와 하나 이상의 부 생성자를 정의할 수 있습니다.
주 생성자
클래스 이름 뒤에 소괄호를 붙혀 간단하게 주 생성자를 만들 수 있습니다.
class Book(
title: String,
totalPage: Int
) {
// ...
}
- 주 생성자는 클래스 이름(Book) 바로 뒤에 소괄호()를 감싸서 만들 수 있으며, 소괄호 안에는 클래스에 주입될 프로퍼티가 나열됩니다.
- 주 생성자에는 var,val 을 사용하여 프로퍼티들을 나열하여 값의 초기화를 할 수 있습니다.
- var, val 로 선언된 Property는 클래스 내/외부에서 접근이 가능합니다.
- var, val 로 프로퍼티가 선언되지 않았다면 생성된 객체의 프로퍼티 접근이 불가능합니다.
class Book(
var title: String,
val totalPage: Int
) {
override fun toString(): String {
return "Book 정보 : title='$title', totalPage=$totalPage)"
}
}
부 생성자
constructor 키워드를 사용해 부 생성자를 만들 수 있습니다. 부 생성자는 부 생성자로 들어온 값을 this()를 사용해서 주 생성자 또는 다른 부 생성자에게 위임을 통해 객체를 생성합니다. 주 생성자가 없는 경우(() 키워드를 쓰지 않고 바로 {}로 클래스를 정의한 경우)에는 this 호출을 하지 않아도 됩니다.
class Book(
var category: String // 주 생성자
) {
var title: String = "무제노트"
var totalPage: Int = 100
// 주 생성자를 호출하는 부 생성자
constructor(category: String, title: String): this(category) {
this.title = title
}
// 다른 부 생성자를 호출하는 부 생성자
constructor(
category: String,
title: String,
totalPage: Int
): this(category, title) {
this.totalPage = totalPage
}
}
Init 키워드
init은 객체가 생성되고 나서 바로 실행됩니다. init은 init 키워드와 중괄호 {}를 사용해서 표현합니다.
보통 init 함수는 객체가 생성되기 전에 Validation의 역할을 위해 존재합니다.
- 예제
class BookWithInitValidation(
val title: String,
val totalPage: Int
) {
init {
if (title.isNullOrEmpty() || totalPage === 0) {
println("값 넣어")
throw IllegalArgumentException("말 좀 들어 좀")
}
}
}
fun main() {
val bboong = BookWithInitValidation(title = "", totalPage = 0)
}
생성자의 접근 제어자
* TIP) 코틀린 접근 제어자의 종류
public - 어디서나 접근 가능(생략하면 public)
private - 동일 클래스 내부에서만 접근 가능
protected - 기본적으로 private, 상속 받은 경우에 자식에서 접근 가능
internal - 같은 모듈 내부에서만 접근 가능
- 기본 생성자는 일반적으로 바로 소괄호()로 감싸는 모양이지만, 사실 앞에 접근제어가 public 과 constructor키워드가 생략된 것입니다.
class Book (
title: String,
totalPage: Int
) {
// ...
}
class Book public constructor(
title: String,
totalPage: Int
) {
// ...
}
- 생성자에 대해 접근제어가 필요하다면 기본 생성자에 constructor 키워드를 명시 해주어야만 합니다.
class Book private constructor(title: String, totalPage: Int) {
}
class Book internal constructor(title: String, totalPage: Int) {
}
class Book protected constructor(title: String, totalPage: Int) {
}
프로퍼티의 접근 제어자
- 생성자의 각 프로퍼티에도 접근제어자를 지정할 수 있습니다.
- 접근제어자를 private으로 두면 클래스 외부에서는 title, totalPage에 접근하지 못합니다.
- 그 외에 protected, internal 로도 지정이 가능합니다.
class PrivateBook(
private val title: String,
private val totalPage: Int,
) {}
fun main() {
val note: PrivateBook = PrivateBook("비밀책", 200)
// note.title 불가능
// note.totalPage 불가능
}
- 프로퍼티 접근 제어자는 디컴파일 될 때 접근 제어자에 따라 자바의 Field 또는 Field/접근자로 변환됩니다.
- 프로퍼티 접근 제어자의 자세한 내용 : 클릭!, 코틀린의 Property를 자바 Field로 디컴파일 해보자
생성자 호출 순서
객체가 생성될 때 각 경우에 따라 생성자가 호출되는 순서는 다음과 같습니다.
주생성자만 있을 때
주 생성자 호출 → init 호출 → 객체 생성
주생성자가 부생성자와 함께 있을 때
- 주 생성자로 초기화 한 경우
주 생성자 호출 → init 호출 → 객체 생성
- 부 생성자로 초기화 한 경우
부 생성자가 바로 주 생성자 호출 → 주 생성자 호출 → init 호출 → 부 생성자 초기화 진행 → 객체 생성
자바의 생성자
코틀린을 학습하다보면 자바는 코틀린과 뗄래야 뗄 수 없는 관계라는 것을 알 수 있습니다. 그 이유는 코틀린이 자바와의 상호 운용성을 고려해서 만들어졌기 때문에 코틀린은 자바의 코딩 스타일을 최대한 반영해서 만들어졌습니다.
처음 보시는 분들은 자바를 모를 수 있기 때문에 간단한 예제를 통해 자바의 생성자는 어떻게 생겼는지 확인하고 코틀린과 비교해보는 목차를 추가했습니다.
코틀린 코드
// 코틀린 문법
class 클래스이름(
접근제어자 변/상수 멤버변수1: 자료형,
접근제어자 변/상수 멤버변수2: 자료형,
) {
// 클래스 본문
}
// 코틀린 클래스 예제
class Book(
public var title: String,
public var totalPage: Int,
) {
// 클래스 본문
}
- 클래스 생성
- 주 생성자 : 코틀린은 () 안에 멤버변수인 프로퍼티를 나열해서(var, val로 선언된 경우에만) 값을 초기화하는 주 생성자를 만듭니다.
- 부 생성자 : 클래스 본문에 프로퍼티를 선언하고 명시적으로 생성자를 정의할 수 있습니다. 또 다른 부 생성자 혹은 주 생성자를 호출하여 값의 초기화를 위임하지만, 위임하지 않는 프로퍼티는 명시적으로 생성한 부 생성자에서 초기화 해야 합니다.
- 접근자
- 코틀린에는 자바의 Getter/Setter라고 불리는 접근자가 없습니다.
- 초기화 블럭
- init 키워드와 {}를 사용하여 인스턴스의 초기화를 진행합니다. 주 생성자가 호출되면 바로 다음에 호출됩니다.
자바 코드
// 자바 문법
접근제어자 class 클래스이름 {
// 멤버 변수
접근제어자 자료형 멤버변수1;
접근제어자 자료형 멤버변수1;
// 생성자
접근제어자 클래스이름() {} // 기본 생성자
접근제어자 클래스이름(자료형 멤버변수1, 자료형 멤버변수2) { // 매개변수가 있는 생성자
this.멤버변수1 = 멤버변수1;
this.멤버변수2 = 멤버변수2;
}
// 접근자
접근제어자 자료형 get멤버변수1() {
return this.멤버변수1;
}
접근제어자 void set멤버변수1(자료형 멤버변수1) {
this.멤버변수1 = 멤버변수1;
}
접근제어자 자료형 get멤버변수2() {
return this.멤버변수2;
}
접근제어자 void set멤버변수2(자료형 멤버변수1) {
this.멤버변수2 = 멤버변수2;
}
// 메서드
}
// 자바 클래스 예제
public class Book {
// 멤버 변수
private String title;
private int totalPage;
// 생성자
public Book(String title, int totalPage) {
this.title = title;
this.totalPage = totalPage;
}
// 접근자
private String getTitle() {
return title;
}
private void setTitle(String title) {
this.title = title;
}
public int getTotalPage() {
return totalPage;
}
public void setTotalPage(int totalPage) {
this.totalPage = totalPage;
}
}
- 클래스 생성
- 기본 생성자 : 자바는 컴파일 될 때 소스파일의 클래스에 생성자가 하나도 없으면 클래스이름() {} 과 같은 기본 생성자를 만듭니다.
- 매개변수가 있는 생성자 : 자바는 메서드처럼 생성자에서 매개변수를 받아 필드의 값을 초기화 할 수 있습니다.
- 또한 자바에서도 this(), this를 사용해서 생성자에서 다른 생성자를 호출할 수 있습니다.
public class Car {
String color;
String gearType;
int door;
// 매개변수가 없는 기본 생성자
Car() {
this("white", "auto", 4);
}
// 매개변수가 color만 있는 생성자
public Car(String color) {
this(color, "auto", 4);
}
// 매개변수가 모두 있는 생성자
public Car(String color, String gearType, int door) {
this.color = color;
this.gearType = gearType;
this.door = door;
}
}
- 접근자
- 자바는 Getter/Setter라고 불리는 접근자가 존재합니다.
3. 초기화 블럭
- static 키워드와 {}를 사용하여 인스턴스의 초기화를 진행합니다. 인스턴스 생성 시점에 생성자보다 먼저 호출됩니다.
정리하기
- 코틀린은 주/부생성자가 있다.
- 생성자 문법은 클래스이름(), constructor() {}, constructor(): this() {}의 형태가 있습니다.
- init 키워드 + {}를 사용해서 주 생성자 호출 직후 초기화 블럭을 호출할 수 있습니다.(생략 가능)
- 생성자에 접근제어자를 선언할 수 있습니다.(public인 경우 생략 가능)
- 멤버변수인 프로퍼티에 접근 제어자를 선언할 수 있습니다.(public인 경우 생략 가능)
- 생성자의 호출순서는 주 -> init -> 부의 순서로 수행됩니다.