Kotlin 변수를 다루는 방법

코틀린을 배우기 위해서 인프런에서 강의를 구매하고 코틀린과 친해지고 기본기를 다지기 위해서 공부하는 중이다. 글 내용은 변수를 다루는 방법이고 최태현님의 자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide) 강의에 소금을 조금 친 내용이다

Person Class

public class Person {
    private final String name;

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

Java에서의 변수

public class JavaMain {
    public static void main(String[] args) {
        long number1 = 10L;
        // 기본형

        final long number2 = 10L;

        Long number3 = 1_000L;
        // 참조형

        long number4 = number2 + number3;
        // 기본형과 참조형의 연산은 기본형으로 변환되어 처리됨, number3는 자동 언박싱됨

        final List<Integer> numbers = Arrays.asList(1, 2);
        numbers.add(3); // UnsupportedOperationException 런타임 에러 발생, final이지만 내부 요소는 변경 가능
        // 요점은 final이 객체의 참조를 변경하지 못하게 할 뿐, 객체 내부의 상태는 변경할 수 있다는 것

        Person person = new Person("혁");
        // 객체 인스턴스화를 위해서 new 키워드를 사용
    }
}

final List<Integer> numbers = Arrays.asList(1, 2);
numbers.add(3);
이 부분이 어렵다 ㅠ AI 도움이 필요하다

final이 제어하는 것: 참조(reference)
final List<Integer> numbers = Arrays.asList(1, 2);

// 이것은 불가능 - 컴파일 에러
numbers = new ArrayList<>(); // 다른 객체로 재할당 시도

// 이것은 가능 - final과 관계 없음
numbers.clear() // 내부 상태 변경
final이 제어하지 않는 것: 객체 내부 상태
final StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb); // "Hello World"

// sb = new StringBuilder(); // 불가능. 참조 변경
변경 가능한 컬렉션
final List<Integer> mutableList = new ArrayList();
mutableList.add(1); // 가능
mutableList.add(2); // 가능
mutableList.remove(0); // 가능

// mutableList = new ArrayList<>(); // 불가능
UnsupportedOperationException의 이유 – Arrays.asList()의 특수성
final List<Integer> fixedList = Arrays.asList(1, 2);

fixedList.add(3); // UnsupportedOperationException
fixedList.remove(0); //UnsupportedOperationException

fixedList.set(0, 10); // 가능. 기존 요소 변경
  • Arrays.asList()는 크기가 고정된 리스트를 반환하기 때문에 add()가 안 된다
final의 역할
  • 참조 변경 방지: 다른 객체 할당 불가능하다
  • 내용 변경 방지: 객체 내부 상태는 여전히 변경 가능하다

아래 코드는 Kotlin에서 변수를 다루는 방법을 나타내는 소스코드이다

// Java는 선 타입 후 변수명
Long number

// Kotlin은 선 변수명 후 타입
number: Long

변수 선언 키워드 – var(바ㄹ)과 val(밸)의 차이점

  • var은 가변으로 변경 가능, val은 불변으로 변경 불가능(read-only)
fun main() {
    var number1 = 10L
    //  Java - long number1 = 10L;
    // 타입을 자동으로 추론해준다.

    var number2: Long = 10L
    // 타입을 명시적으로 지정할 수 있다.

    val number3 = 10L
    // Java - final long number3 = 10L;

    val number4: Long = 10L

    // var number5 컴파일 에러 - 타입을 추론할 수 없음
    var number5: Long // 명시적 타입 지정으로 해결

    // println(number5) 컴파일 에러 - 초기화 전 사용 불가
    number5 = 10L // 초기화 후
    println(number5) // 사용 가능

    val number6: Long
    number6 = 10L
    // val은 불변 이지만 최초 한 번은 값을 넣어줄 수 있다.
    println(number6)

    // val 컬렌셕에는 element를 추가할 수 있다
    // val은 참조를 고정하지만, 객체 내부 상태는 변경 가능
    val list = mutableListOf(1, 2, 3)

    list.add(4) // 가능 - 내부 상태 변경
    // list = mutableListOf(5, 6) 불가능 - 참조 변경
}
  • Kotlin의 Tip 중 하나는 모든 변수는 우선 val로 선언하고, 변경이 필요한 경우에만 var로 선언(변경)하면 코드들이 더 깔끔해지고 디버깅도 쉬워 진다

primitive type(기본형)과 reference type(참조형)

  • Java에서는 primitive type과 reference type이 구분되지만, Kotlin에서는 primitive type과 reference type이 구분되지 않는다
  • Java에서는 연산을 할 경우 reference type을 primitive type으로 변환(boxing, unboxing)해야 하지만, Kotlin에서는 내부적으로 자동으로 변환된다
    • boxing: int → Integer 처럼 primitive type을 참조 타입으로 감싸는 것
    • unboxing: Integer → int 처럼 참조 타입에서 원시값을 꺼내는 것
    • boxing은 감싸기, unboxing은 꺼내기
  • Kotlin에서는 연산을 할 경우 내부적으로 primitive type으로 변환되어 연산이 수행된다
  • Kotlin에서는 boxing, unboxing이 필요하지 않다 (Kotlin이 알아서 처리해준다)
fun main() {
    var number7 = 10L
    var number8 = 1_000L
    var number9 = number7 + number8
    // Kotlin에서는 primitive와 wrapper 구분 없이 자동 처리
}

nullable type

  • Kotlin에서는 null을 허용하는 타입과 허용하지 않는 타입이 구분된다
  • null을 허용하는 타입은 ?를 붙여서 선언한다
  • Java에서는 null을 허용하는 타입과 허용하지 않는 타입이 구분되지 않지만, Kotlin에서는 null을 허용하는 타입과 허용하지 않는 타입이 구분된다
fun main() {
    // var number10 = 1_000L
    // number10 = null
    // null 안됨

    // var number11: Long = 1_000L
    // number11 = null
    // null 안됨

    var number12: Long? = 1_000L
    number12 = null
    // 사용 시에는 null 체크 필요: number12?.toString()
}

객체 인스턴스화

  • Java에서는 new 키워드를 사용하지만, Kotlin에서는 new 키워드를 사용하지 않고 객체를 인스턴스화한다
fun main () {
    val person = Person("혁")
    // 객체 인스턴스화를 위해서 new가 필요하지 않음
}

출처 – 인프런 강의 중 자바 개발자를 위한 코틀린 입문(Java to Kotlin Starter Guide)