본문 바로가기

프로그래밍 언어 기초/KOTLIN

[Kotlin] 객체지향 프로그래밍 - 클래스, 생성자, init, 접근 제한자

개요

오늘은 코틀린의 클래스에 대해서 알아보도록 하겠습니다.

코틀린은 객체지향 프로그래밍 언어(Object-Oriented Programming Language)입니다.

오늘날 존재하는 모든 객체지향 프로그래밍 언어는 객체(Object)를 중심으로 프로그램이 설계됩니다.

프로그램을 실행하는 주체가 객체(Object)라는 개념은,

절차지향 프로그래밍 언어와 객체지향 프로그래밍 언어의 차이를 구분하는 중요한 기준점이 됩니다.

더 자세한 내용은 아래의 포스트를 참고하시면 좋을 것 같습니다.


https://1-hee.tistory.com/94

 

[CS BASIC] 프로그램과 프로그래밍 그리고 프로그램 패러다임

오늘은 프로그램과 프로그래밍, 그리고 프로그래밍 패러다임에 대해서 다뤄보도록 하겠습니다. 요즈음에는 다양한 경로를 통해 프로그래밍을 접할 수 있고 그래서 누구나 마음만 먹으면 프로

1-hee.tistory.com

 


코틀린의 객체, 클래스(Class)

코틀린에서 클래스는 아래와 같이 선언할 수 있습니다.

class Customer;
class Contact(val id:Int, var:email:String)

fun main(args:Array<String>){
    val customer  = Customer();
    val contact = Contact(1, "jhon@gmail.com");

    println(contact.email); // jhon@gmail.com
    contact.email = "anne@gmail.com" // jhon@gmail.com -> anne@gmail.com
}

 

코틀린은 문법적인 부분에서 클래스 선언이 매우 간결해졌습니다.

아무런 프로퍼티와 메서드를 갖지 않는 클래스를 선언할 때는 Customer와 같이 선언하여 사용할 수 있습니다.

여기에 추가로, 클래스에 프로퍼티를 추가하여 생성할 수도 있는데

이 경우에는 위 코드 중 Contact 클래스처럼 선언하여 사용할 수 있습니다.

모든 객체는 자기 자신을 생성하기 위한 도구 즉, 생성자(Constructor)가 필요합니다.

개발자는 생성자(constructor)를 통해서 다양한 객체를 인스턴스화 할 수 있으며

프로그램이 설계한 대로 동작하도록 활용할 수 있습니다.


그런데, 위 코드에서 클래스 Customer에서는 아무리 찾아봐도 생성자(constructor)를 찾을 수 없습니다.
그런데도 코드를 실행했을 때, 위 코드는 정상적으로 잘 작동합니다.

그 이유는 무엇일까요? 이는 바로 컴파일러(Compiler) 덕분입니다.

코틀린뿐만 아니라, 자바(Java)와 같은 객체지향 프로그래밍 언어에서는

각 프로그래밍 언어로 작성된 코드 파일은 그 자체만으로는 컴퓨터가 읽고 해석할 수 없습니다.

그렇기에 컴파일러는 사람이 읽고 이해할 수 있는 코드 파일에서 컴퓨터가 읽을 수 있는 형태로 변환해주는 역할을 담당합니다.

이때 컴파일러는 소스 코드를 분석하면서 동시에 객체의 경우 생성자를 살펴보게 되는데,

만약 Customer와 같은 클래스가 선언되었다면, 개발자가 명시적으로 선언하지 않았더라도 생성자를 알아서 만들어 줍니다.

그렇기에, 코틀린을 개발자는 위와 같은 상황에서 생성자를 생략하여 사용할 수 있습니다.


프로퍼티와 메서드 직접 선언하기

코틀린에서는 간결한 클래스 선언을 지원하지만, 동시에 개발자의 입맛대로 다양하게 클래스를 커스텀할 수도 있습니다.

아래의 코드를 한번 살펴보겠습니다.

class Student {
    var name:String
    var age:Int

    fun sayName(){
        pritnln("Hi, My name is $name ")
    }
}

 

Student 클래스에서는 name과 age 등 클래스가 소유한 변수, 즉 프로퍼티를 가지고 있습니다.

sayName()이라는 함수, 즉 메서드를 가지고 있습니다.

이처럼 클래스는 자기 자신의 상태를 표현하기 위해, 클래스 내부 또는 외부에서 사용할 수 있는 변수나, 함수가 가질 수 있는데요.

이때 클래스마다 내부에 가지고 있는 변수를 프로퍼티, 함수를 메서드 라고 부릅니다.

프로퍼티와 메서드에 대해 정확히 정의하기 위해서는 조금 더 자세한 설명이 필요하지만,

이번 포스팅에서는 빠른 이해와 설명의 편의를 위해 이처럼 간단하게만 정의하겠습니다.


주 생성자(Primary Constructor)

코틀린에서 모든 클래스는 자기 자신을 인스턴스로 만들기 위한 생성자(constructor)가 1개 이상 존재합니다.

클래스의 생성자는 해당 객체의 프로퍼티 등을 초기화하는 방법을 제공하는 도구입니다.

 

그런데, 이러한 생성자 중에서 개발자의 의도에 따라 해당 클래스를 초기화하기 위한 주된 방법을 명시할 수도 있습니다.

이때 어떤 클래스를 초기화하기 위한 주요한 생성자로 특정한 생성자를 일컬어 주 생성자(Primary Constructor)라고 합니다.

코틀린에서 생성자는 클래스마다 한 개씩만 명시할 수 있습니다.

아래의 코드는 코틀린 클래스에서 주 생성자를 명시하는 가장 간단한 방법의 하나입니다.

 

class Contact(val id:Int, var email:String);

 

코틀린에서는 클래스 Contact처럼 클래스의 이름 옆에 소괄호()를 통해 주 생성자를 명시할 수 있습니다.

또한, 위와 같이 주 생성자에 val나 var 키워드와 함께 파라미터를 선언하면 각각의 파라미터는 해당 클래스의 프로퍼티로 사용할 수 있습니다.

 

class Contact(id:Int, email:String){
    val id:Int = id
    val email:String = email
}

 


코틀린에서는 위와 같이 주 생성자와 초기화 코드를 분리해낼 수도 있습니다.

이 경우에는 Contact 클래스의 주 생성자가 명시되었지만 주 생성자에서 곧바로 프로퍼티의 선언이 이루어지는 것은 아닙니다.

 

그러나, 코틀린에서 주 생성자는 각 클래스 상태를 초기화하는 목적을 위해 설계된 생성자이기 때문에

위의 사례에서 주 생성자와 초기화 로직을 분리한 것과 분리하지 않은 것은 결과적으로 모두 같습니다.

이외에도 코틀린은 주 생성자를 선언하는 방법을 한 가지 더 제공합니다.

 

class Dog constructor(name: String, age: Int) {
    var name:String = name
    var age:Int = age;

    fun bark(){
        println("[$name, $age] : bow-wow")
    }
}

 


클래스 Dog는 클래스 옆에 소괄호()를 통해 주 생성자를 명시하진 않았습니다.

그러나, 코틀린에서 생성자를 의미하는 constructor 키워드를 통해 생성자를 클래스 이름 옆에 명시해두었습니다.

이 경우에도 위와 마찬가지로 클래스 Dog는 주 생성자를 명시한 클래스가 됩니다.

주 생성자를 명시할 때 var나 val을 사용하지 않으면,  생성자에서 받아온 파라미터를 

클래스 내부에서 프로퍼티 선언 시에 초기화 값으로 이어줄 수 있습니다.

 

즉 클래스를 생성함과 동시에 생성자로부터 받아오는 매개변수를 통해

클래스의 프로퍼티를 초기화시켜주는 일을 한꺼번에 처리할 수 있다는 것을 의미합니다.

하지만 여기서 주의할 점은 주 생성자의 파라미터를 통해 프로퍼티를 초기화할 때,

등호를 기준으로 왼쪽과 오른쪽이 가리키는 대상에 서로 다르다는 점입니다.

위에 작성된 클래스 Dog을 예시로 들어보도록 하겠습니다.

클래스 Dog는 등호(=)를 기준으로 왼쪽에 작성된 name은 Dog 클래스의 프로퍼티 name을 가리킵니다.

반면에, 오른쪽에 작성된 name은 생성자로부터 받아온 파라미터 name을 가리킵니다.

따라서, 이를 아래와 같이 가시적으로 구분하여 작성할 수도 있습니다.

 

class Dog constructor(a: String, b: Int) {
    var name:String = a;
    var age:Int = b;

    fun bark(){
        println("[$name, $age] : bow-wow")
    }
}



하지만, 클래스의 생성자는 클래스의 프로퍼티를 초기화하는 것이 주된 목적이므로,

일반적으로는 생성자에서 받아오는 파라미터의 이름은 클래스 내부에서 다루는 프로퍼티와 비슷하도록 맞춰줍니다.


보조 생성자

코틀린에서 클래스 내부에 명시한 주 생성자를 제외한 모든 생성자를 일컬어 보조 생성자라 합니다.

코틀린 클래스에서 주 생성자는 클래스별로 단 한 개만 명시할 수 있지만, 보조 생성자는 여러 개 작성할 수 있습니다.

 

아래의 코드를 한번 살펴보겠습니다.

class Student {
    var name: String = ""
    var age: Int = 0

    constructor(name: String, age: Int) : this() {
        this.name = name
        this.age = age
    }

    constructor(name: String):this(){
        this.name = name
    }

    constructor() {
    }

    fun sayName() {
        println("Hi, My name is $name")
    }
}

 

위 코드에서 클래스 Student의 주 생성자는 무엇일까요?

정답은 '주 생성자가 없다.' 입니다.

클래스 Student는 코틀린의 주 생성자 문법을 통해 주 생성자를 명시하지 않았습니다.

따라서, Student 클래스는 주 생성자가 없고 보조 생성자가 3개인 클래스가 됩니다.

코틀린 컴파일러는 각 클래스별로 1개 이상의 생성자가 있는지 점검합니다.

만약 생성자가 하나도 없을 경우에는 파라미터가 0개인 생성자를 한 개 만들어 줍니다.

 

클래스 Student에서도 이와 같은 형태인 생성자가 있습니다.

바로, constructor() {} 부분입니다.

이처럼 생성자 중에서 파라미터가 없는 생성자를 일컬어 기본 생성자라고 합니다.

코틀린 컴파일러는 생성자가 선언되지 않은 클래스에서 컴파일 시에 기본 생성자를 알아서 생성해줍니다.


보조 생성자와 this

코틀린의 생성자는 기본적으로 함수와 그 형태가 유사하지만, 함수의 이름이 없고 반환 타입이 없다는 것이 특징입니다.

그렇기에 일반 함수에 비해서 클래스의 생성자는 그 구분이 함수보다 다소 까다로울 수 있습니다.

그런데도 코틀린에서는 여러 개의 생성자를 만들 수 있다는 사실은

코틀린 컴파일러가 생성자를 구분하는 방법이 있다는 것을 방증합니다.

생성자에는 여러 개의 파라미터와 각 파라미터의 타입이 명시되어 있습니다.

코틀린 컴파일러는 바로 이 부분을 기준으로 각 생성자를 구분할 수 있습니다.

이때 생성자에서 다루는 파라미터의 개수와 타입의 조합을 일컬어 생성자 시그니처라고 합니다.

그리고 코틀린 컴파일러는 이 생성자 시그니처를 바탕으로 생성자를 구분할 수 있습니다.

 

class Test {
    var a:Int = 0;
    var b:Int = 0;
    var c:Boolean = false;

    constructor(a:Int):this(a>0){ // constructor 1
        println("call a...");
        this.a = a;
        this.b = 0;
    }
    constructor(a:Int, b:Int):this(a){ // constructor 2
        println("call ab...");
        this.a = a;
        this.b = b;
    }

    constructor(c:Boolean){ // constructor 3
        println("call c...");
        this.c = c;
    }
}

fun main(args:Array<String>){
    val test = Test(1);
    println(test.a);
}



위의 코드에서 클래스 Test는 보조 생성자를 3개 가지고 있습니다.

메인 함수에서 Test 클래스의 인스턴스를 생성할 때, 주어지는 값이 정수이므로 생성자 constructor 1을 호출합니다.

코틀린에서 생성자는 this 키워드를 통해 다른 생성자를 호출할 수 있습니다.

이때 constructor 1이 this를 통해 다른 생성자를 호출할 때 넘기는 값이 불리언(Boolean)이므로

다른 생성자 중에서 constructor 3을 호출합니다.

 

constructor 3에서는 다른 생성자를 호출하지 않으므로, 아래와 같은 결과가 출력됩니다.

 

call c...
call a...

 

 

이처럼 코틀린의 생성자는 생성자의 시그니처로 구분이 가능하므로,

위와 같이 this 키워드를 통해 다른 생성자를 호출하여 인스턴스의 초기화 과정을 구성할 수 있습니다.


init

코틀린은 클래스의 인스턴스가 생성될 때 초기화 등에 사용할 수 있는 기능을 제공합니다.

코틀린에서 클래스의 인스턴스 생성 시 초기화에 사용될 수 있는 블록을 init 블록이라고 하며

init 블록은 코틀린 클래스 내부에서 작성되어야 합니다.

init 블록은 클래스의 프로퍼티 초기화나 다른 초기화 작업을 수행하는 데 사용될 수 있습니다.

여러 개의 init 블록을 사용하여 클래스의 초기화 작업을 여러 단계로 나눌 수도 있습니다.

init 블록은 클래스의 인스턴스가 생성될 때 호출되며, 주 생성자와 함께 사용될 수 있습니다.

주로 클래스의 프로퍼티를 초기화하거나, 인스턴스 생성 시 필요한 초기화 작업을 수행하는 데 사용되는 문법입니다.

다음은 init 블록의 간단한 예시입니다.

 

class MyClass(val name: String, val age: Int) {
    init {
        // 클래스의 인스턴스가 생성될 때 호출되는 초기화 블록
        println("Initializing MyClass with name $name and age $age")
    }
}

fun main() {
    val myObj = MyClass("John", 30) // Initializing MyClass with name John and age 30
}

 

 


접근 제한자 Access Modifier

코틀린 클래스를 구성하는 프로퍼티와 메서드는 다른 클래스에서 자유롭게 참조하게 할 수도 있고,

특정 프로퍼티나 메서드에 대해서 접근 권한을 제한할 수도 있습니다.

이처럼 어떤 클래스를 구성하는 프로퍼티와 메서드에 접근 권한을 명시한 것을 접근 제한자(Access Modifier)라고 합니다.

코틀린에서는 접근 제한자를 다음과 같이 4가지를 제공합니다.

 

접근 제한자(Access Modifier) 설명
public 모든 곳에서 접근할 수 있습니다.
코틀린에서의 기본 접근 제한자입니다.
즉, 접근 제한자를 명시적으로 지정하지 않으면 public으로 간주합니다.
internal 같은 모듈 내에서만 접근할 수 있습니다.
모듈은 컴파일 시에 같은 컴파일 유닛을 의미합니다.
코틀린에서는 일반적으로 프로젝트를 구성하는 각각의 모듈을 의미합니다.
protected 클래스 내부 및 하위 클래스에서 접근할 수 있습니다.
즉, 해당 멤버를 선언한 클래스 내부에서 그리고 해당 클래스를 상속한 하위 클래스에서만 접근할 수 있습니다.
private 해당 멤버를 선언한 클래스 내부에서만 접근할 수 있습니다.
즉, 외부에서는 접근할 수 없으며, 하위 클래스에서도 접근할 수 없습니다.


이러한 접근 제한자들은 코드의 가시성과 모듈성을 유지하고 클래스 및 모듈의 내부 구현을 숨기는 데 도움을 줍니다. 

적절한 접근 제한자를 사용하면 클래스나 함수의 의도된 사용 범위를 명확히 표현할 수 있고,

결과적으로 프로그램의 안전성과 유지보수성을 높일 수 있습니다.

class AccessModifier{
    val publicValue1 = 1;
    public val publicValue2 = 2;
    internal val interValue = 3;
    protected val protectValue = 4;
    private val privateValue = 5;
}

 

 


 

참고 자료

https://kotlinlang.org/docs/home.html

 

Kotlin Docs | Kotlin

 

kotlinlang.org