코틀린을 배우기 위해서 인프런에서 강의를 구매하고 코틀린과 친해지고 기본기를 다지기 위해서 공부하는 중이다. 글 내용은 변수를 다루는 방법이고 최태현님의 자바 개발자를 위한 코틀린 입문(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)