코틀린 파헤치기/2부. 코틀린 객체지향

[코파기 2부] 9. 코틀린과 정적 (Object/Companion Object)

유노 Uknow 2023. 3. 15. 16:53

 

 

정적(static) ?

정적이란, 고정되어 있다는 의미입니다. 동적(Dynamic)과 상반된 의미를 갖고 있죠.

정적 변수란, 해당 클래스의 모든 객체가 하나의 데이터를 공유한다는 특성이 있습니다.

 

자바 코드를 예시로 들어보겠습니다.

// 자바 코드
class Test {
    static int a = 0; // 정적(static)으로 생성
    int b = 0;
}

public class Main {
    public static void main(String[] args) {
        Test test1 = new Test();
        Test test2 = new Test();

        test1.a = 1;
        test1.b = 1;

        test2.a = 2;
        test2.b = 2;

        System.out.println("test1 a : " + test1.a); // test1의 a 변경
        System.out.println("test1 b : " + test1.b); // test1의 b 변경
        
        System.out.println("test2 a : " + test2.a); // test2의 a 변경
        System.out.println("test2 b : " + test2.b); // test2의 b 변경
    }
}


// 출력
>>> test1 a : 2
test1 b : 1
test2 a : 2
test2 b : 2

 

test1의 a, b를 1로,

test2의 a, b를 2로 변경했습니다.

 

test1 a : 2

est1 b : 1

test2 a : 2

test2 b : 2

 

분명, test2의 a만 2로 변경하였는데, test1의 a도 2로 변경되어 있습니다.

이 처럼, 정적 변수를 사용하면 모든 클래스가 같은 변수를 공유합니다.

 

 

코틀린에는 static 키워드가 없다

자바의 경우, static을 사용해 정적 멤버들을 다룰 수 있었습니다.

하지만 코틀린에서는 static 키워드가 없습니다.

 

그렇다면, 코틀린에서는 정적인 걸 어떻게 처리할까요?

 

 

const val

바로 직전에 나왔던 const val은 자바의 public static final 과 같은 역할을 합니다.

 

const val MAX = 1000000000
const val STRING = "Hello World"
const val CHAR = 'A'
const val CHECK = true

fun main() {
    
}

위와 같이 사용이 가능합니다.

하지만 const val은 상수로써, 값을 바꾸는 것이 불가능합니다.

 

그럼, 자바처럼 정적인 변수를 선언하려면 어떻게 해야 할까요?

 

 

Object

object Test {
    var a = 1
    var b = 2
}

fun main() {
    println(Test.a)
    println(Test.b)

    Test.a = 3
    Test.b = 4

    println(Test.a)
    println(Test.b)
}

// 출력
>> 1
2
3
4

 

class (클래스명) 대신, object (클래스명)처럼 사용한다면,

자바의 static class와 유사하게 쓸 수 있습니다.

 

 

Companion Object

class Test {
    var a = 1

    companion object {
        var b = 1
        
        fun printB() {
            println(b)
        }
    }
}

fun main() {
    val test1 = Test()
    test1.a = 2

    val test2 = Test()
    test2.a = 3

    // companion object는 (객체명.변수명)이 아닌 (클래스명.변수명) 으로 접근 가능
    Test.b = 2
    // test1.b = 3  객체명.변수명으로는 접근 불가

    println(test1.a) // 3
    println(test2.a) // 3
    println(Test.b) // 2
    Test.printB() // 2
}


// 출력
>>> 2
3
2
2

 

혹은 클래스 안에 companion object를 두어,

킅래스 안에서 정적 변수나 메소드를 선언하는 것도 가능합니다.

 

 

부록. companion object는 사실 static이 아니다

 

 

왜 키워드 이름이 companion일까???

자바의 extends 처럼,

언어의 설계자가 별 의미 없이 키워드의 이름을 지었을리 없다고 생각한 저는

관련 자료들을 찾아보았습니다.

 

 

class Test {
    var a = 1

    companion object {
        var b = 1

        fun printB() {
            println(b)
        }
    }
}

fun main() {
    println(Test.Companion.b)
}

// 출력
1

 

companion object는 사실 static과 비슷하게 쓰일 뿐, static이 아닙니다.

fun main()에서 Test.Companion.b의 형태로 Test의 companion Object의 b에 접근하였습니다.

다만, 언어 차원에서 Companion을 생략해도 되게 만들었을 뿐이죠.

 

Test 클래스가 생성될 때, 함께 생성되는 동반자(Companion) 객체인 것입니다.

int, boolean, char 등 Primmitive 타입까지 모두 코틀린에서는 객체이듯이,

static과는 달리 companion object 역시 객체인 것입니다.

 

--

 

코파기 2부 객체지향 편도 이걸로 끝났습니다.

객체지향 요소가 들어가 있어 1부에 비해 꽤 오래 준비했던 것 같습니다.

준비하면서 공부도 굉장히 많이 됐던 것 같아요.

 

자바/코틀린을 처음 배울 때, 이걸 왜 쓰지? 어따 쓰지? 하는 생각을 자주 했는데요.

이러한 생각을 저만 가지지 않을 것이라 생각하여

 

그동안 개발을 하며 겪은 개인적인 생각이나, 경험,

그리고 수많은 개발자 선배들의 발자취를 뒤져가며 이에 대한 내용을 부록을 정리하곤 했습니다.

몇몇 편은 오히려 부록을 더 열심히 준비하기도 했던 것 같네요.

 

3부는 코틀린 고급편으로,

코틀린 요소 중 기본편에서 다루지 않은 것들을 다뤄보려 합니다.