반응형
해당 글에서는 Kotlin의 주요 특징 및 문법 중 변수, 데이터 타입, 캐스팅, NULL 처리 방법에 대해 알아봅니다
1) Kotlin
💡 Kotlin
- JetBrains에서 개발한 프로그래밍 언어이며, ‘Java Virtual Machine (JVM)’에서 실행이 되며 Java 코드와 100% 호환되는 객체 지향언어입니다.
- ‘타입 추론’을 지원하는 정적 언어이며, 코드가 컴파일될 때 타입이 결정되어 런타임 중에 발생할 수 있는 오류에 대해 컴파일 단계에서 사전에 방지할 수 있습니다.
[ 더 알아보기 ]
💡 타입 추론(Type Inference)
- Kotlin에서 지원하는 기능으로 변수의 타입을 명시적으로 선언할 필요가 없습니다. 이는 Kotlin 컴파일러가 초기화 값을 기반으로 변수의 타입을 자동으로 추론합니다.
- 그러나, 모두 타입 추론을 할 수 없으며 명시적 타입 선언이 필요한 경우가 있습니다.
- 변수를 선언할 때 초기화하지 않는 경우
- 컴파일러가 타입을 정확히 추론하지 못하는 경우
- 특정 타입으로 명시적으로 지정하고 싶은 경우
val name = "John" // String 타입으로 추론됨
var age = 30 // Int 타입으로 추론됨
var result: Double // 초기화하지 않을 때는 타입을 명시해야 함
val number: Int = 5 // 명시적으로 Int 타입으로 지정
1. Kotlin 주요 특징
특징 | 설명 |
JVM 호환성 | Java Virtual Machine (JVM) 위에서 실행되며, 기존 Java 코드와 100% 호환됩니다. Java 프로젝트에 Kotlin을 점진적으로 도입할 수 있습니다. |
타입 추론 | 변수의 타입을 명시적으로 선언할 필요가 없이 Kotlin 컴파일러가 초기화 값을 기반으로 변수의 타입을 자동으로 추론합니다. |
세미콜론 생략 | 문장 끝에 세미콜론(;)을 생략할 수 있습니다. 이는 코드의 간결성을 높이고 가독성을 향상시킵니다. |
간결한 문법 | Java보다 더 간결하고 표현력이 뛰어난 문법을 제공합니다. 코드의 가독성을 높이고 개발 시간을 단축시킵니다. |
Null 안정성 | 타입 시스템이 NullPointerException을 방지하기 위해 설계되었습니다. 더 안전한 코드를 작성할 수 있습니다. |
함수형 프로그래밍 | 함수형 프로그래밍을 지원하며, 람다 표현식, 고차 함수, 불변성 등의 기능을 제공합니다. |
안드로이드 개발 | 2017년부터 Google이 안드로이드 개발의 공식 언어로 채택했습니다. 안드로이드 앱 개발에서 Kotlin의 사용이 크게 증가했습니다. |
다중 플랫폼 지원 | JVM, JavaScript, Native 코드로 컴파일이 가능합니다. 웹, 모바일, 데스크톱 등 다양한 플랫폼에서 사용할 수 있습니다. |
코루틴 지원 | 비동기 프로그래밍을 위한 코루틴을 기본적으로 지원합니다. 복잡한 비동기 작업을 더 쉽게 처리할 수 있습니다. |
2. Kotlin 빌드 과정
💡 Kotlin 빌드 과정
1. Kotlin 소스 코드 작성 : .kt
- 개발자가 .kt 확장자를 가진 Kotlin 소스 파일을 작성합니다.
2. Kotlin 컴파일러 실행 : Kotlin Complier
- Kotlin 컴파일러(kotlinc)가 소스 코드를 분석하고 중간 형태인 바이트코드로 변환합니다.
3. 바이트코드 생성 : .class
- 컴파일 결과로 .class 파일이 생성됩니다. 이 파일은 Java 바이트코드와 동일한 형식입니다.
4. JVM에서 실행
- 생성된 바이트코드는 Java Virtual Machine (JVM)에서 실행됩니다. JVM은 바이트코드를 해석하고 실행합니다.
5. 최적화 및 실행
- JVM은 실행 중 Just-In-Time (JIT) 컴파일을 통해 바이트코드를 기계어로 변환하여 성능을 최적화합니다.
💡 [참고] Kotlin과 Java에 대한 비교가 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
[ 더 알아보기 ]
💡 Kotlin to Java 혹은 Java to Kotlin이 수행될까?
- Kotlin 소스코드는 Java와 동일하게 JVM에 의해 컴파일되어 수행되기에 Kotlin 프로젝트 내에서 Java 언어와 병행하여 사용이 가능하기에 설정을 통해 서로 호출하여 사용하기가 가능합니다.
- 또한, IntelliJ IDEA의 자동변환 기능을 통해서 IntelliJ IDEA는 Kotlin 파일을 Java로, 또는 Java 파일을 Kotlin으로 자동 변환하는 기능을 제공합니다. 파일을 열고 'Code' 메뉴에서 'Convert Java File to Kotlin File' 또는 그 반대를 선택할 수 있습니다.
- 해당 변환방법이 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
2) Kotlin 변수(Variables)키워드와 데이터 타입(Basic types)
1. Kotlin 변수(Variables) 키워드
💡 Kotlin 변수(Variables) 키워드
- 기본적으로 변수의 키워드는 '변수 이름'의 앞에 붙여서 선언을 합니다.
// [기본 구조-1] 변수 타입을 지정하지 않은 경우 + 타입 추론
val / var <변수 이름> = 변수 값
// 기본 구조 예시 -1
val userId = "adjh54";
var userNm = "lee"
// or
// [기본 구조-2] 변수 타입을 지정한 경우 + 명시적으로 변수 타입 선언
val / var <변수 이름> : 타입 = 변수 값
// 기본 구조 예시 -2
val userId : String = "adjh54";
var userNm : String = "lee";
변수의 키워드 종류 | 설명 | 예시 |
val | 불변(immutable) 변수. 한 번 할당되면 값을 변경할 수 없습니다 | val name: String = "John" |
var | 가변(mutable) 변수. 값을 변경할 수 있습니다. | var age: Int = 25 |
const val | 컴파일 시점에 결정되는 상수. 최상위 레벨이나 object 내부에서만 사용이 가능합니다. | const val PI = 3.14 |
lateinit var | 나중에 초기화할 수 있는 변수. null이 될 수 없는 타입에만 사용이 가능합니다. | lateinit var person: Person |
lazy | 처음 사용될 때 초기화되는 지연 초기화 변수입니다. | val lazyValue: String by lazy { "Hello" } |
[ 더 알아보기 ]
💡 불변 변수 키워드 val이랑 가변 변수 키워드 var 중에 모두 다 var를 써도 상관 없지 않은가?
- 아닙니다. 불변 변수 키워드인 val를 사용하였을때 아래와 같은 이점을 가지고 있습니다.
1. 불변성(Immutability)으로 인한 코드의 안정성 향상
2. 스레드 안전성 보장
3. 의도치 않은 값 변경 방지
4. 코드 가독성과 유지보수성 향상
💡 스마트 캐스트(Smart Casts)
- Kotlin의 강력한 기능 중 하나로, 컴파일러가 특정 조건에서 자동으로 타입을 캐스팅해 주는 기능입니다.
- 이를 통해 개발자는 명시적인 타입 캐스팅을 줄일 수 있어 코드의 가독성과 안전성이 향상됩니다.
fun demo(x: Any) {
if (x is String) {
print(x.length) // x는 자동으로 String으로 캐스팅됨
}
}
fun nullSafeDemo(str: String?) {
if (str != null) {
print(str.length) // str은 자동으로 non-null String으로 캐스팅됨
}
}
2. Kotlin 데이터 타입(Data Type)
💡 Kotlin 데이터 타입(Data Type)
- Kotlin에서는 아래와 같이 Numbers, Boolean, Characters, Strings, Array, Any, Noting, Unit 등의 타입들을 가집니다.
💡 [참고] 데이터 타입의 요약
분류 | 데이터 타입 | 설명 | 크기/범위 |
정수형 | Byte, Short, Int, Long | 정수를 저장하는 타입 | 8비트 ~ 64비트 |
부호 없는 정수형 | UByte, UShort, UInt, ULong | 양수만 저장하는 타입 | 8비트 ~ 64비트 |
실수형 | Float, Double | 소수점이 있는 실수를 저장 | 32비트, 64비트 |
논리형 | Boolean | true/false 값 저장 | 1비트 |
문자형 | Char | 단일 문자 저장 | 16비트 |
문자열 | String | 문자열 저장 | 제한 없음 |
배열 | Array | 같은 타입의 데이터 모음 | 가변적 |
특수 | Any, Nothing, Unit | 최상위/최하위 타입, 반환값 없음 | - |
[ 더 알아보기 ]
💡 Kotlin에서는 Java와 같이 기본 자료형이 분리되지 않았네?
- Kotlin은 기본 자료형을 내부적으로 사용하지만 개발자가 직접 구분하지는 않습니다.
- Kotlin 컴파일러를 통해서 적절한 기본 자료형 또는 래퍼클래스로 처리하기 때문입니다.
💡 [참고] Java에서 기본 자료형, 참조 자료형, 래퍼 클래스에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
2.1. Numbers Type
💡 Numbers Type
- Kotlin에서는 숫자를 나타내는 여러 데이터 타입을 제공합니다.
숫자 데이터 타입 | 분류 | 데이터 저장 단위 | 범위 |
Byte | 정수 | 8비트 | -128 ~ 127 |
Short | 정수 | 16비트 | -32768 ~ 32767 |
Int | 정수 | 32비트 | -2,147,483,648 (-2^31) ~ 2,147,483,647 (2^31 - 1) |
Long | 정수 | 64비트 | -9,223,372,036,854,775,808 (-2^63) ~ 9,223,372,036,854,775,807 (2^63 - 1) |
UByte | 부호 없는 정수 | 8비트 | 0 ~ 255 |
UShort | 부호 없는 정수 | 16비트 | 0 ~ 65,535 |
UInt | 부호 없는 정수 | 32비트 | 0 ~ 4,294,967,295 (2^32 - 1) |
ULong | 부호 없는 정수 | 64비트 | 0 ~ 18,446,744,073,709,551,615 (2^64 - 1) |
Float | 부동소수점 | 32비트 | 약 ±3.4 x 10^-38 ~ ±3.4 x 10^38 |
Double | 부동소수점 | 64비트 | 약 ±1.8 x 10^-308 ~ ±1.8 x 10^308 |
package com.blog.kotlinspringbootform.component
import org.springframework.stereotype.Component
@Component
class dataTypeComponent {
fun numberType() {
// Byte 예시
val b: Byte = 100
// Short 예시
val s: Short = 10000
// Int 예시
val age: Int = 30
// Long 예시
val population: Long = 7000000000
// UByte 예시
val ub: UByte = 255u
// UShort 예시
val us: UShort = 65535u
// UInt 예시
val ui: UInt = 4294967295u
// ULong 예시
val ul: ULong = 18446744073709551615u
// Float 예시
val pi: Float = 3.14f
// Double 예시
val e: Double = 2.71828
}
}
2.2. Boolean Type
💡 Boolean Type
- Kotlin에서 Boolean은 true와 false 두 가지 값만을 가질 수 있는 데이터 타입입니다.
진위형 데이터 타입 | 설명 | 범위 |
Boolean | true 또는 false 값 | true, false |
package com.blog.kotlinspringbootform.component
import org.springframework.stereotype.Component
@Component
class dataTypeComponent {
/**
* 진위형 관련 변수들
*/
fun booleanType() {
// Boolean 예시
val isKotlinFun: Boolean = true
}
}
[ 더 알아보기 ]
💡 Boolean 연산자에는 무엇이 있는가?
- && (AND): 두 조건이 모두 true일 때 true 반환합니다.
- || (OR): 두 조건 중 하나라도 true이면 true 반환합니다.
- !(NOT): true를 false로, false를 true로 변환합니다.
2.3. Characters & Strings & Arrays Type
💡Characters & Strings & Arrays Type
- Characters (Char): 단일 문자를 저장하는 데이터 타입을 의미합니다.
- Strings (String): 문자열을 저장하는 데이터 타입을 의미합니다.
- Arrays (Array): 고정된 크기의 데이터 집합을 저장하는 데이터 타입을 의미합니다.
분류 | 데이터 타입 | 설명 | 범위 |
Characters | Char | 16비트 유니코드 문자 | U+0000 ~ U+FFFF |
Strings | String | 문자열 | 제한 없음 |
Arrays | Array | 고정 크기 배열 | 제한 없음 |
package com.blog.kotlinspringbootform.component
import org.springframework.stereotype.Component
@Component
class dataTypeComponent {
/**
* 문자형 & 문자열형 & 배열형 관련 변수들
*/
fun charType () {
// Char 예시
val initial: Char = 'A'
// String 예시
val name: String = "Kotlin"
// Array 예시
val numbers: Array<Int> = arrayOf(1, 2, 3)
}
}
2.4. Others Type
분류 | 데이터 타입 | 설명 | 범위 |
Any | Any | 모든 타입의 상위 타입 | 모든 타입 |
Nothing | Nothing | 모든 타입의 하위 타입, 값이 없음 | 값 없음 |
Unit | Unit | 값이 없는 함수의 반환 타입 | Unit |
package com.blog.kotlinspringbootform.component
import org.springframework.stereotype.Component
@Component
class dataTypeComponent {
/**
* 기타 타입 관련 변수들
*/
fun otherType() {
// Any 예시
val obj: Any = "Hello"
// Nothing 예시
fun fail(): Nothing = throw Exception("Failed")
// Unit 예시
fun printHello(): Unit {
println("Hello")
}
}
}
3. Kotlin Collection Type
💡 Collection Type
- Kotlin에서 사용하는 Collection Type에 대해서 List, Set, Map이 존재합니다.
- 이러한 컬렉션 타입에서도 읽기 전용(read-only) 인터페이스와 가변(mutable) 인터페이스로 나뉩니다.
3.1. Collection Type : Interface
💡 Collection Type : Interface
- Kotlin에서 데이터 컬렉션을 다루기 위한 기본적인 계약(contract)을 정의하는 인터페이스입니다. 이는 컬렉션의 기본 동작과 기능을 명시합니다.
- 해당 Interface에서는 읽기 전용(Read-only) 인터페이스와 가변(Mutable) 인터페이스가 있습니다.
1. 읽기 전용(Read-only) 인터페이스
- 데이터를 조회만 할 수 있고 수정할 수 없는 인터페이스입니다. Collection, List, Set, Map이 여기에 속합니다.
2. 가변(Mutable) 인터페이스
- 데이터를 조회하고 수정할 수 있는 인터페이스입니다. MutableCollection, MutableList, MutableSet, MutableMap이 여기에 속합니다.
컬렉션 | 분류 | 설명 |
Set | 읽기 전용(read-only) | 중복되지 않는 요소들의 집합입니다 |
List | 읽기 전용(read-only) | 순서가 있는 요소들의 컬렉션입니다 |
Map | 읽기 전용(read-only) | 키-값 쌍의 컬렉션입니다 |
Sequence | 읽기 전용(read-only) | 지연 평가되는 컬렉션으로, 대량의 데이터를 처리할 때 효율적입니다 |
MutableSet | 가변(mutable) 인터페이스 | 요소를 추가하거나 제거할 수 있는 Set 입니다 |
MutableList | 가변(mutable) 인터페이스 | 요소를 추가, 제거, 수정할 수 있는 List 입니다 |
MutableMap | 가변(mutable) 인터페이스 | 키-값 쌍을 추가, 제거, 수정할 수 있는 Map 입니다 |
ArrayDeque | 가변(mutable) 인터페이스 | 양쪽 끝에서 요소를 추가하거나 제거할 수 있는 double-ended queue입니다 |
package com.blog.kotlinspringbootform.component.dataType
import org.springframework.stereotype.Component
@Component
class CollectionComponent {
/**
* 초기화, 읽기만 가능한 Collection
*/
fun readCollection(username: String, password: String) {
// Set 초기화
val readOnlySet = setOf(1, 2, 3)
// List 초기화
val readOnlyList = listOf("apple", "banana", "cherry")
// Map 초기화
val readOnlyMap = mapOf("a" to 1, "b" to 2, "c" to 3)
// 이후에는 이 컬렉션들을 변경할 수 없습니다.
// readOnlyList.add("date") // 컴파일 에러
}
/**
* 초기화, 읽기, 수정, 삭제가 가능한 Collection
*/
fun mutableCollection() {
// MutableSet 사용
val mutableSet = mutableSetOf(1, 2, 3)
mutableSet.add(4) // 가능
// MutableList 사용
val mutableList = mutableListOf("apple", "banana", "cherry")
mutableList.add("date") // 가능
// MutableMap 사용
val mutableMap = mutableMapOf("a" to 1, "b" to 2)
mutableMap["c"] = 3 // 가능
}
/**
* Collection 빈 상태로 선언합니다.
*/
fun emptyCollection() {
val emptySet = emptySet<Int>();
val emptyList = emptyList<String>();
val emptyMap = emptyMap<String, Int>()
}
/**
* Collection Build를 이용하여 선언 및 값을 초기화합니다.
*/
fun buildCollection() {
val map = buildMap { //
put("a", 1)
put("b", 0)
put("c", 4)
}
println(map)
val list = buildList {
add("hello")
add("world")
}
val set = buildSet {
add("hello")
add("world")
}
}
}
3.2. Collection Type : 인터페이스 구현체
컬렉션 | 분류 | 설명 |
ArrayList | List 구현체 : 가변(mutable) 인터페이스 | 동적 크기 조정이 가능한 배열 리스트입니다 |
LinkedList | List 구현체 : 가변(mutable) 인터페이스 | 이중 연결 리스트로 구현된 리스트입니다 |
HashSet | Set 구현체 : 가변(mutable) 인터페이스 | 해시 테이블을 사용하는 Set 구현체입니다 |
TreeSet | Set 구현체 : 가변(mutable) 인터페이스 | 정렬된 Set 구현체입니다 |
HashMap | Map 구현체 : 가변(mutable) 인터페이스 | 해시 테이블 기반의 Map 구현체입니다 |
TreeMap | Map 구현체 : 가변(mutable) 인터페이스 | 정렬된 Map 구현체입니다 |
@Component
class CollectionComponent {
/**
* Collection Type 구현체를 사용하는 예시
*/
fun collectionImplements() {
val arrayList = ArrayList<String>() // 동적 크기 조정이 가능한 리스트
arrayList.add("첫 번째")
arrayList.add("두 번째")
val linkedList = LinkedList<Int>() // 이중 연결 리스트
linkedList.add(1)
linkedList.add(2)
// Set 예제
val hashSet = HashSet<String>() // 해시 테이블 기반 Set
hashSet.add("사과")
hashSet.add("사과") // 중복된 값은 추가되지 않음
val treeSet = TreeSet<Int>() // 정렬된 Set
treeSet.add(3)
treeSet.add(1)
treeSet.add(2)
println(treeSet) // [1, 2, 3] 순서로 출력
// Map 예제
val hashMap = HashMap<String, Int>() // 해시 테이블 기반 Map
hashMap["one"] = 1
hashMap["two"] = 2
val treeMap = TreeMap<String, Int>() // 정렬된 Map
treeMap["b"] = 2
treeMap["a"] = 1
treeMap["c"] = 3
println(treeMap) // 키를 기준으로 정렬되어 출력
}
}
💡 [참고] Java에서 Collection Framework에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
3) Kotlin 데이터 타입 변환 : 캐스팅(Casting)
💡 Kotlin 데이터 타입 변환: 캐스팅(Casting)
- Kotlin에서는 타입 캐스팅을 위해 두 가지 주요 연산자를 제공합니다. 'as'와 'is' 연산자를 통해 데이터 형 변환을 합니다.
- 또한, 기본 타입 간의 변환을 위한 메서드도 제공합니다.
데이터 타입 변환 방법 | 설명 | 예시 |
기본 타입 변환 메서드 | 기본 타입 간 변환을 위한 메서드 | val longNum: Long = intNum.toLong() |
as 연산자 | 안전하지 않은 명시적 캐스팅. 실패 시 ClassCastException 발생 | val str: String = obj as String |
as? 연산자 | 안전한 캐스팅. 실패 시 null 반환 | val str: String? = obj as? String |
is 연산자 | 타입 검사. 스마트 캐스트 적용 | if (obj is String) { println(obj.length) } |
💡 [참고] Java의 형변환에 대해 궁금하시면 아래의 글을 참고하시면 도움이 됩니다.
1. 기본 타입 변환
💡 기본 타입 변환
- 기본 타입 간의 변환을 위해 아래의 메서드를 제공합니다.
메서드 | 설명 | 예시 |
toByte() | Byte로 변환 | val byteNum: Byte = intNum.toByte() |
toShort() | Short로 변환 | val shortNum: Short = intNum.toShort() |
toInt() | Int로 변환 | val intNum: Int = longNum.toInt() |
toLong() | Long으로 변환 | val longNum: Long = intNum.toLong() |
toFloat() | Float로 변환 | val floatNum: Float = intNum.toFloat() |
toDouble() | Double로 변환 | val doubleNum: Double = intNum.toDouble() |
toChar() | Char로 변환 | val charValue: Char = intNum.toChar() |
toString() | String으로 변환 | val stringNum: String = intNum.toString() |
toByteOrNull() | String을 Byte로 변환 시도, 실패 시 null 반환 | val byte: Byte? = "100".toByteOrNull() |
toShortOrNull() | String을 Short로 변환 시도, 실패 시 null 반환 | val short: Short? = "1000".toShortOrNull() |
toLongOrNull() | String을 Long으로 변환 시도, 실패 시 null 반환 | val long: Long? = "1234567890".toLongOrNull() |
toFloatOrNull() | String을 Float로 변환 시도, 실패 시 null 반환 | val float: Float? = "3.14".toFloatOrNull() |
toDoubleOrNull() | String을 Double로 변환 시도, 실패 시 null 반환 | val double: Double? = "3.14159".toDoubleOrNull() |
@Component
class CastingComponent {
/**
* 기본 타입의 캐스팅 방법
*/
fun normalCasting() {
val intNum = 10
val longNum = 1000L
val floatNum = 3.14f
// Int를 다른 타입으로 변환
val byteNum: Byte = intNum.toByte()
val shortNum: Short = intNum.toShort()
val longFromInt: Long = intNum.toLong()
val floatFromInt: Float = intNum.toFloat()
val doubleFromInt: Double = intNum.toDouble()
val charFromInt: Char = intNum.toChar()
val stringFromInt: String = intNum.toString()
// Long을 Int로 변환
val intFromLong: Int = longNum.toInt()
// Float를 Int로 변환
val intFromFloat: Int = floatNum.toInt()
}
/**
* 안전하지 않은 as 캐스팅
*/
fun asCasting() {
val obj: Any = "Hello"
val str: String = obj as String // "Hello"
val num: Int = obj as Int // ClassCastException
}
}
2. as 연산자 : 안전하지 않은 캐스팅
💡 as 연산자 : 안전하지 않은 캐스팅
- 'as' 연산자는 타입을 명시적으로 캐스팅할 때 사용합니다. 하지만 캐스팅이 실패하면 ClassCastException이 발생할 수 있습니다.
@Component
class CastingComponent {
/**
* 안전하지 않은 as 캐스팅
*/
fun asCasting() {
val obj: Any = "Hello"
val str: String = obj as String // "Hello"
val num: Int = obj as Int // ClassCastException
}
}
3. as? 연산자 : 안전한 캐스팅
💡 as? 연산자 (안전한 캐스팅)
- 'as?' 연산자는 안전한 캐스팅을 수행합니다. 캐스팅이 실패하면 null을 반환합니다.
@Component
class CastingComponent {
/**
* 안전한 as 캐스팅
*/
fun asSafeCasting() {
val obj: Any = "Hello"
val str: String? = obj as? String // "Hello"
val num: Int? = obj as? Int // null
}
}
3. is 연산자 : 타입 검사
💡 is 연산자 (타입 검사)
- 'is' 연산자는 객체가 특정 타입인지 검사합니다. 'is'를 사용한 후에는 스마트 캐스트가 적용됩니다.
@Component
class CastingComponent {
/**
* is 캐스팅
*/
fun isCasting() {
val obj: Any = "Hello"
if (obj is String) {
println(obj.length) // obj는 자동으로 String으로 캐스트됨
}
}
}
4) Kotlin Non-Null / Nullable 체크
💡 Kotlin Non-Null/ Nullable 체크
- Non-Null과 Nullable은 변수가 null 값을 가질 수 있는지 없는지를 명시적으로 구분하는 개념입니다.
- 이는 Kotlin의 타입 시스템의 중요한 특징 중 하나로, NullPointerException을 방지하는 데 도움을 줍니다.
1. Non-Null 타입
💡 Non-Null 타입
- 기본적으로 Kotlin의 모든 타입은 non-null입니다. 즉, null 값을 가질 수 없습니다.
var name: String = "John"
name = null // 컴파일 오류
2. Nullable 타입
💡 Nullable 타입
- 변수가 null 값을 가질 수 있도록 하려면, 타입 뒤에 물음표(?)를 붙여 nullable 타입으로 선언합니다.
var name: String? = "John"
name = null // 정상 동작
3. Null 안전성 기능
3.1. 안전 호출 연산자 (?.)
💡 안전 호출 연산자 (?.)
- 객체가 null이 아닌 경우에만 메서드나 속성에 접근합니다.
val length = name?.length // name이 null이면 length도 null
3.2. 엘비스 연산자 (?:)
💡 엘비스 연산자 (?:)
- 좌변이 null이 아니면 좌변을, null이면 우변을 반환합니다.
val length = name?.length ?: 0 // name이 null이면 0 반환
3.3. not-null 단언 연산자 (!!)
💡 not-null 단언 연산자 (!!)
- nullable 타입을 강제로 non-null로 취급합니다. null인 경우 NullPointerException 발생.
val length = name!!.length // name이 null이면 NullPointerException 발생
5) Kotlin 해당 글 주요 개념 요약
개념 | 설명 | 예시 |
타입 추론 | 컴파일러가 초기화 값을 기반으로 변수 타입을 자동으로 추론 | val name = "John" // String으로 자동 추론 |
기본 타입 캐스팅 | 타입 변환 함수를 사용한 명시적 변환 | toInt(), toString(), toDouble() 등 |
as 연산자 | 명시적 타입 캐스팅 (안전하지 않음) | val str: String = obj as String |
as? 연산자 | 안전한 타입 캐스팅 (실패 시 null 반환) | val str: String? = obj as? String |
is 연산자 | 타입 검사 및 스마트 캐스트 | if (obj is String) { ... } |
Non-Null 타입 | 기본적으로 null을 허용하지 않는 타입 | var name: String = "John" |
Nullable 타입 | null을 허용하는 타입 (?로 표시) | var name: String? = null |
안전 호출 연산자 (?.) | null이 아닐 때만 메서드/속성 접근 | name?.length |
엘비스 연산자 (?:) | null일 경우 대체값 제공 | name?.length ?: 0 |
not-null 단언 (!!) | 강제로 non-null 처리 (위험) | name!!.length |
/**
* Kotlin의 주요 개념들을 실제 코드로 보여주는 예제
*/
class KotlinConcepts {
fun typeInference() {
// 타입 추론 예제
val name = "John" // String으로 자동 추론
val age = 25 // Int로 자동 추론
val height = 175.5 // Double로 자동 추론
}
fun typeCasting() {
// 기본 타입 캐스팅
val number = "123"
val intValue = number.toInt() // String을 Int로 변환
val doubleValue = intValue.toDouble() // Int를 Double로 변환
// as 연산자 사용
val any: Any = "Hello"
val str: String = any as String // 안전하지 않은 캐스팅
// as? 연산자 사용 (안전한 캐스팅)
val safeStr: String? = any as? String // 실패시 null 반환
// is 연산자와 스마트 캐스트
if (any is String) {
println(any.length) // 자동으로 String으로 캐스트됨
}
}
fun nullSafety() {
// Non-Null 타입
var nonNullName: String = "John"
// nonNullName = null // 컴파일 에러!
// Nullable 타입
var nullableName: String? = "John"
nullableName = null // 가능!
// 안전 호출 연산자 (?)
val length = nullableName?.length // null이면 length도 null
// 엘비스 연산자 (?:)
val len = nullableName?.length ?: 0 // null이면 0 반환
// not-null 단언 연산자 (!!)
// 주의: null이면 NullPointerException 발생
val forcedLength = nullableName!!.length
}
}
오늘도 감사합니다. 😀
반응형
'Kotlin > 이해하기' 카테고리의 다른 글
[Kotlin] Kotlin 기본 문법 이해하기 -3 : 클래스, 인터페이스, 구현체 (0) | 2024.11.23 |
---|---|
[Kotlin] Kotlin 기본 문법 이해하기 -2 : Control flow (조건식, RANGE, 반복문) (0) | 2024.11.23 |
[Kotlin] Kotlin, Java 언어를 대체 할 수 있을까? : 기술동향, 정의, 흐름, 특징 (0) | 2023.03.20 |
[Kotlin] 앱 아키텍처 패턴(MVC, MVP, MVVM) 이해하기 (0) | 2022.09.20 |
[Kotlin] 소스코드 스타일 / 명명 규칙 이해하기 (0) | 2022.09.19 |