Developing Myself Everyday
article thumbnail

내장 자료형


코틀린에서 기본으로 제공하는 클래스를 내장 자료형이라고 합니다. 

 

 

 

Number(숫자) 클래스


아래는 코틀린에서 제공하는 숫자 클래스의 상위 클래스인 Number 클래스입니다.

public abstract class Number {
    /**
     * Returns the value of this number as a [Double], which may involve rounding.
     */
    public abstract fun toDouble(): Double

    /**
     * Returns the value of this number as a [Float], which may involve rounding.
     */
    public abstract fun toFloat(): Float

    /**
     * Returns the value of this number as a [Long], which may involve rounding or truncation.
     */
    public abstract fun toLong(): Long

    /**
     * Returns the value of this number as an [Int], which may involve rounding or truncation.
     */
    public abstract fun toInt(): Int

    /**
     * Returns the [Char] with the numeric value equal to this number, truncated to 16 bits if appropriate.
     */
//    @Deprecated("Direct conversion to Char is deprecated. Use toInt().toChar() or Char constructor instead.", ReplaceWith("this.toInt().toChar()"))
    public abstract fun toChar(): Char

    /**
     * Returns the value of this number as a [Short], which may involve rounding or truncation.
     */
    public abstract fun toShort(): Short

    /**
     * Returns the value of this number as a [Byte], which may involve rounding or truncation.
     */
    public abstract fun toByte(): Byte
}

Number 클래스는 추상 클래스이므로 직접 인스턴스화할 수 없습니다. 대신, 이 클래스를 상속하는 구체적인 숫자 유형 클래스의 인스턴스를 생성할 수 있습니다.

추상 클래스란?
추상 클래스는 직접 인스턴스화 할 수 없고 다른 클래스의 상위 역할만 할 수 있는 클래스입니다. 클래스를 추상 클래스로 만들기 위해서는 abstract라는 변경자 키워드를 붙여야 합니다.
인스턴스화란?
인스터스화란 클래스를 기반으로 실제 메모리에 실체화된 개별 객체를 생성하는 과정을 의미합니다.

 

 

 

KClass

KClass는 리플렉션 기능을 활용하여 클래스의 속성, 메서드, 생성자 등의 정보를 동적으로 검사하거나 조작할 수 있는 방법을 제공합니다.

 

Kotlin에서 KClass를 사용해 클래스의 정보를 얻을 수 있으며, 다양한 리플렉션 연산을 수행할 수 있습니다.

 

아래는 클래스의 KClass를 출력하는 예제입니다.

fun main() {
    val sample: KClass<Int> = Int::class
    println("Class: ${sample}") // 출력: Class: class kotlin.Int   
}

이 외에도 다양한 KClass의 메서드를 사용할 수 있습니다.

 

 

 

NaN(Not  a  Number)

실수는 숫자를 정확히 모를때는 무한대를(INFINITY)를 사용하고, 수학적으로 정의되지 않은 값을 나타낼 때에는 NaN을 사용합니다.

fun main() {
    val result = 0.0 / 0.0 // 0을 0으로 나누는 연산
    println("Result: $result") // 출력: NaN

    val nanValue = Double.NaN
    println("Is NaN? ${nanValue.isNaN()}") // 출력: Is NaN? true
}

 

 

 

 

문자열


StringBuilder

`String` 문자열은 기본적으로 변경할 수 없는 객체입니다. 따라서 내부 원소를 추가하거나 변경하는 수정 작업을 한다면, 기존 문자열을 변경하는 것이 아니라 새로운 문자열을 갖는 `String`을 생성 후 참조를 바꾸는 형식을 가집니다.

 

다만 이런 수정 작업을 거치면 변경하기 전의 기존의 값이 남게 됩니다. 이때 사용되는 것이 `StringBuilder` 입니다. `StringBuilder`를 사용하면 새로운 문자열 객체를 생성하는 대신 기존 객체를 변경하여 작업을 수행할 수 있습니다. 

 

아래는 `StringBuilder`를 사용할 때의 성능을 비교하는 코드입니다.

import kotlin.system.measureTimeMillis

fun stringBuilder(iterations: Int): String {
    val stringBuilder = StringBuilder()
    for (i in 1..iterations) {
        stringBuilder.append("Iteration $i\n")
    }
    return stringBuilder.toString()
}

fun string(iterations: Int): String {
    var result = ""
    for (i in 1..iterations) {
        result += "Iteration $i\n"
    }
    return result
}

fun main() {
    val iterations = 10000

    val timeWithStringBuilder = measureTimeMillis {
        val result = stringBuilder(iterations)
        println("Using StringBuilder: ${result.length} characters")
    }
    println("Time taken with StringBuilder: $timeWithStringBuilder ms")

    val timeWithStringPlus = measureTimeMillis {
        val result = string(iterations)
        println("Using String plus: ${result.length} characters")
    }
    println("Time taken with String plus: $timeWithStringPlus ms")
}

// 출력
// Using StringBuilder: 148894 characters
// Time taken with StringBuilder: 7 ms
// Using String plus: 148894 characters
// Time taken with String plus: 499 ms

 

 

`StringBuilder`의 다양한 메서드를 사용해서 문자열을 추가, 삽입, 변경, 삭제 등을 할 수 있습니다.

 

  1. append 메서드: 문자열을 뒤에 추가합니다.
  2. insert 메서드: 특정 위치에 문자열을 삽입합니다.
  3. replace 메서드: 지정한 범위 내의 문자열을 다른 문자열로 변경합니다.
  4. delete 메서드: 지정한 범위 내의 문자열을 삭제합니다.
  5. reverse 메서드: 문자열을 역순으로 뒤집습니다.
fun main() {
    val stringBuilder = StringBuilder()

    // 문자열 추가
    stringBuilder.append("Hello, ")
    stringBuilder.append("world!")
    println(stringBuilder.toString())

    // 문자열 삽입
    stringBuilder.insert(7, "beautiful ")
    println(stringBuilder.toString())

    // 문자열 변경
    stringBuilder.replace(0, 5, "Hi")
    println(stringBuilder.toString())

    // 문자열 삭제
    stringBuilder.delete(2, 4)
    println(stringBuilder.toString())

    // 문자열 반전
    stringBuilder.reverse()
    println(stringBuilder.toString())
}

// 출력
// Hello, world!
// Hello, beautiful world!
// Hi, beautiful world!
// Hibeautiful world!
// !dlrow lufituaebiH

 

 

 

문자열의 구조

문자열은 일련의 문자(Char)들의 시퀀스로 구성되어 있습니다. 그렇기에 Kotlin에선 문자열을 List처럼 사용할 수 있습니다.

 

따라서 문자열의 문자에 인덱스를 사용해 접근할 수 있고 내부 순환을 제공합니다.

fun main() {
    val text = "Hello, Kotlin!"

    // 인덱스를 사용하여 문자열에 접근
    val firstChar = text[0]
    val lastChar = text[text.length - 1]

    println("First character: $firstChar") // 출력: First character: H
    println("Last character: $lastChar") // 출력: Last character: !

    // 문자열 내부를 순환하며 각 문자 출력
    for (char in text) {
        print("$char ") // 출력: H e l l o ,   K o t l i n ! 
    }
}

 

 

 

문자열의 `compareTo`

문자열에서 `compareTo` 메서드는 `ignoreCase` 매개변수를 사용해 대소문자를 무시하고 비교할 지 여부를 결정할 수 있습니다. `ignoreCase` 매개변수를 사용하면 kotlin.text` 패키지의 `StringsKt` 클래스의 `compareTo` 함수가 호출되어 대소문자를 무시하고 비교하게 됩니다.

fun main() {
    val str1 = "Hello"
    val str2 = "hello"

    val result1 = str1.compareTo(str2)

    println("Not Using ignoreCase: $result1") // 출력: Not Using ignoreCase: -32

    val result2 = str1.compareTo(str2, true)

    val a = 10
    a.compareTo(20)
    println("Using ignoreCase: $result2") // 출력: Using ignoreCase: 0
}

 

 

 

문자열 분해 / 결합

문자열을 분해하고 결합할 수 있는 함수가 있습니다.

 

`split` 함수는 문자열을 특정 구분자를 기준으로 분할하여 리스트로 반환합니다. 분할된 각 부분은 리스트의 원소가 됩니다.

'joinToString` 함수는 리스트의 원소들을 결합하여 하나의 문자열로 만듭니다. 원하는 구분자를 지정할 수 있습니다. 

 

fun main() {
    val text = "H.e.l.l.o"

    val stringList : List<String> = text.split(".")

    for (string in stringList) {
        print("$string ") // 출력: H e l l o
    }
    println()

    val combinedString: String = stringList.joinToString("")

    println(combinedString) // 출력: Hello
}

 

 

 

 

Any, Unit, Nothing 클래스


Any 클래스

Any 클래스는 코틀린 클래스 계층 구조의 최상위 클래스로 코틀린에서 정의하는 모든 클래스는 암묵적으로 Any 클래스를 상속받습니다. 즉 코틀린 클래스의 superclass입니다.

 

아래는 Any 클래스입니다.

public open class Any {
    /**
     * 이 객체와 다른 객체가 "동등한지(equals)" 여부를 나타냅니다. 구현은 다음 요구 사항을 충족해야 합니다:
     *
     * * 반사성(Reflexive): 모든 비-널 값 `x`에 대해 `x.equals(x)`는 `true`를 반환해야 합니다.
     * * 대칭성(Symmetric): 모든 비-널 값 `x`와 `y`에 대해 `x.equals(y)`는 `true`를 반환하면 `y.equals(x)`가 `true`를 반환해야 합니다.
     * * 추이성(Transitive): 모든 비-널 값 `x`, `y`, `z`에 대해 `x.equals(y)`가 `true`이고 `y.equals(z)`가 `true`라면 `x.equals(z)`는 `true`를 반환해야 합니다.
     * * 일관성(Consistent): 모든 비-널 값 `x`와 `y`에 대해 `x.equals(y)`의 다중 호출은 객체의 `equals` 비교에 사용된 정보가 수정되지 않는 한 항상 일관되게 `true` 또는 일관되게 `false`를 반환해야 합니다.
     * * null과는 절대로 동등하지 않음: 모든 비-널 값 `x`에 대해 `x.equals(null)`는 `false`를 반환해야 합니다.
     *
     * [Kotlin에서 동등성](https://kotlinlang.org/docs/reference/equality.html)에 대해 더 읽어보세요.
     */
    public open operator fun equals(other: Any?): Boolean

    /**
     * 객체의 해시 코드 값을 반환합니다. `hashCode`의 일반적인 계약은 다음과 같습니다:
     *
     * * 객체에서 동일한 객체에 대해 두 번 이상 호출되더라도 `hashCode` 메서드는 `equals` 비교에 사용된 정보가 수정되지 않는 한 항상 동일한 정수를 일관되게 반환해야 합니다.
     * * 두 객체가 `equals()` 메서드에 따라 동등하다면 두 객체 중 각각에 대해 `hashCode` 메서드를 호출하면 동일한 정수 결과가 생성되어야 합니다.
     */
    public open fun hashCode(): Int

    /**
     * 객체의 문자열 표현을 반환합니다.
     */
    public open fun toString(): String
}

 

 

 

Unit 클래스

함수가 의미 있는 값을 반환하지 않는 경우 Unit을 반환 자료형으로 반환합니다. 이는 자바의 `void`와 유사합니다. 코틀린에서는 Unit을 반환하는 함수는 반환 타입을 명시하지 않아도 됩니다.

 

아래는 Unit 클래스입니다.

public object Unit {
    override fun toString() = "kotlin.Unit"
}

Unit 객체는 싱글톤 객체로, 프로그램 내에서 단 하나의 인스턴스만 존재합니다. 따라서 Unit 객체를 생성하는 것은 불가능하며, 이미 정의된 싱글톤 인스턴스를 사용합니다.

 

Unit 클래스는 Any 클래스로부터 상속받은 `toString` 메서드를 오버라이딩해 "kotlin.Unit" 이라는 문자열을 반환하고 있습니다.

 

 

우리가 많이 사용하는 println 함수도 Unit 클래스의 객체를 반환하고 있습니다.

@kotlin.internal.InlineOnly
public actual inline fun println() {
    System.out.println()
}


fun main() {
    print(println()) // 출력 : kotlin.Unit
}

 

 

 

Nothing 클래스

함수를 반환할 때 "절대로 존재하지 않는 값"을 표시하기 위해 Nothing을 사용합니다. 주로 함수가 예외를 던지거나 비정상적으로 종료되는 것을 나타낼 때 사용됩니다.

 

아래는 Nothing 클래스입니다.

public class Nothing private constructor()

 

Nothing 클래스에는 인스턴스가 존재하지 않습니다. 

fun alwaysThrowException(message: String): Nothing {
    throw RuntimeException(message)
}

fun main() {
    try {
        val result = alwaysThrowException("An error occurred")
        println("Result: $result") // 이 부분은 실행되지 않음
    } catch (e: Exception) {
        println("Caught exception: ${e.message}")
    }
}

위의 예시에서 `alwaysThrowException` 함수는 항상 `RuntimeException` 예외를 던집니다. 따라서 `alwaysThrowException` 함수는 Nothing을 반환하며, 실행 중인 코드 블록은 예외가 발생하여 정상적으로 값을 반환하지 않습니다.

 

 

 

 

배열(Array)


배열은 동일한 타입의 요소들을 순서대로 저장하는 선형 자료구조입니다.

 

아래에서 배열 클래스에서 확인할 수 있듯이 배열은 객체를 생성할 때 배열의 크기초기화 함수를 받아 배열을 생성하고 각 요소를 초기화합니다. 그렇기에 배열은 고정된 크기를 가집니다.

public class Array<T> {

    public inline constructor(size: Int, init: (Int) -> T)

    public operator fun get(index: Int): T

    public operator fun set(index: Int, value: T): Unit

    public val size: Int

    public operator fun iterator(): Iterator<T>
}

 

 

 

 

배열 생성

배열을 생성할 때는 위에서 본 생성자를 이용해야 합니다.

public inline constructor(size: Int, init: (Int) -> T)

 

생성자를 사용해서 배열을 생성한 코드는 아래와 같습니다.

val intArray = Array(5, { it })

 

다만 이는 가독성이 매우 떨어집니다. 코틀린에서는 함수의 마지막 인자로 람다 표현식을 사용한다면 중괄호를 괄호 밖으로 빼내고 간결하게 표현할 수 있습니다.

fun main() {
    val intArray = Array(5) { it }

    val iterator: Iterator<Int> = intArray.iterator()

    while (iterator.hasNext()) {
        print("${iterator.next()} ") // 출력: 0 1 2 3 4
    }
}

 

 

 

배열은 Kotlin 표준 라이브러리의 내부 함수인 `arrayOf` 를 사용해서 생성할 수도 있습니다. 함수 내부에서는 입력으로 전달된 초기 값들을 사용하여 배열을 생성하고 초기화 합니다. 배열의 크기는 초기 값들의 개수에 따라 결정됩니다. 

 

`arrayOf` 를 사용한 코드는 아래와 같습니다.

fun main() {
    val intArray = arrayOf(0, 1, 2, 3, 4)

    val iterator: Iterator<Int> = intArray.iterator()

    while (iterator.hasNext()) {
        print("${iterator.next()} ") // 출력: 0 1 2 3 4
    }
}

 

 

 

배열 조회 및 갱신

배열 내의 원소를 검색하거나 갱신할 수도 있습니다. 그러기 아래의 배열 클래스 메서드를 사용하면 됩니다.

public operator fun get(index: Int): T

public operator fun set(index: Int, value: T): Unit

 

코틀린에서는 `[]` 연산자를 사용해 인덱스 접근을 지원하기에 이를 사용하면 더 간편하게 조회 및 갱신을 수행할 수 있습니다.

fun main() {
    val intArray = arrayOf(0, 1, 2, 3, 4)

//    println(intArray.get(3))
    println(intArray[3]) // 출력: 3

//    intArray.set(3, 5)
    intArray[3] = 5
    println(intArray[3]) // 출력: 5
}

 

 

 

배열에 원소 추가

배열의 원소를 추가하는 것은 조금 다른 문제입니다. 이전에 말했듯이 배열은 고정된 크기를 가지기에 배열에 원소를 추가하는 것은 불가능합니다. 그렇기에 코틀린에서는 새로운 배열을 생성해 기존 배열의 내용을 복사하고 원소를 추가해 반환합니다.

 

배열의 원소는 `plus` 확장 함수를 사용해 추가할 수 있으며 이를 수행하는 코드는 아래와 같습니다.

// plus 확장 함수
public actual operator fun IntArray.plus(elements: Collection<Int>): IntArray {
    var index = size
    val result = java.util.Arrays.copyOf(this, index + elements.size)
    for (element in elements) result[index++] = element
    return result
}

fun main() {
    val intArray = arrayOf(0, 1, 2, 3, 4)

    intArray.plus(5)
}

 

 

 

배열의 순서 변경

배열을 정렬하거나, 역정렬, 역순으로 바꿀 수 있습니다. 이를 위해서는 `sort()`, `sortDescending()`, `reverse()` 메서드가 필요합니다.

fun main() {
    val intArray = arrayOf(5, 2, 4, 1, 3)

    // 배열 정렬 (오름차순)
    intArray.sort()
    for (element in intArray) {
        print("$element ") // 출력: 1 2 3 4 5 
    }
    println()

    // 배열 정렬 (내림차순)
    intArray.sortDescending()
    for (element in intArray) {
        print("$element ") // 출력: 5 4 3 2 1
    }
    println()

    // 배열 역순으로 변경
    intArray.reverse()
    for (element in intArray) {
        print("$element ") // 출력: 1 2 3 4 5 
    }
    println()
}

 

배열의 원소를 정렬하면서 새로운 배열을 만들때는 `sorted()`, `sortedDescending()`, `reversed()` 메서드가 필요합니다. 이 메서드들을 사용하면 기존 배열의 값은 그대로 유지됩니다.

fun main() {
    val intArray = arrayOf(5, 2, 4, 1, 3)

    val sortedArray = intArray.sorted()

    // 기존 배열
    for (element in intArray) {
        print("$element ") // 출력: 5 2 4 1 3
    }
    println()

    // 정렬된 배열
    for (element in sortedArray) {
        print("$element ") // 출력: 1 2 3 4 5
    }
    println()
}

 

 

 

 

자료형 처리


Nullable 여부

Nullable은 null값을 허용하는 타입을 의미합니다. Nullable 타입을 사용하면 명시적으로 변수가 null 값을 가질 수 있음을 나타낼 수 있습니다.

 

Nullable 타입은 기존 자료형에 물음표(?)를 붙여서 표시합니다.

 

 

연산자

해당 자료형의 프로퍼티나 메서드에 접근하려면 Safe Call를 사용해야 합니다. Safe Call는 (?.) 연산자를 사용해 변수가 null인 경우에도 예외가 발생하지 않고 결과가 null이 됩니다.

fun main() {
    // Nullable 타입 선언
    val nullableString: String? = "Hello, Kotlin"

    // Safe Call 연산자 사용
    val length: Int? = nullableString?.length
    
    println("문자열의 길이: $length") // 출력: 문자열의 길이: 13
}

 

 

변수가 null인 경우에 결과가 null이 아니라 특정한 값을 지정하고 싶을 수도 있습니다. 이를 지원하는 것이 엘비스(?:) 연산자 입니다. 이 연산자는 null인 경우 대체 값을 제공합니다.

fun main() {
    // Nullable 타입 선언
    val nullableString: String? = null

    // 단언 연산자 사용
    val length: Int = nullableString?.length ?: -1

    println("문자열의 길이: $length") // 출력: 문자열의 길이: -1
}

 

 

만약 변수나 표현식이 null이 아니라고 단언할 수 있다면 단언 연산자(!!)를 사용할 수 있습니다. 단언 연산자를 사용한 값을 변수에 할당하면 그 변수는 Nullable 하지 않습니다.

fun main() {
    // Nullable 타입 선언
    val nullableString: String? = "Hello, Kotlin"

    // 단언 연산자 사용
    val length: Int = nullableString!!.length

    println("문자열의 길이: $length") // 출력: 문자열의 길이: 13
}

 

 

`is` 연산자

`is` 연산자를 사용해 객체가 특정 클래스 타입에 속하는지 검사할 수 있습니다. `obj is Type` 형태로 사용하며, 만약 objType 클래스의 인스턴스라면 true를 반환하고, 그렇지 않다면 false를 반환합니다.

fun main() {
    val value: Any = "Hello, Kotlin!"

    if (value is String) {
        println("It's a String")
        println(value)
    } else {
        println("It's not a String")
    }
}

// 출력: It's a String
//       Hello, Kotlin!

 

 

`as` 연산자

`as` 연산자는 객체를 특정 타입으로 캐스팅(변환)합니다. `obj as Type` 형태로 사용하며, objType 클래스의 인스턴스일 경우 해당 객체를 Type으로 캐스팅합니다. 만약 캐스팅이 불가능한 경우 ClassCastException이 발생할 수 있습니다.

 

Safe Call을 사용해서 obj Type 클래스의 인스턴스가 아닐 경우에 오류를 발생하지 않고 null을 반환할 수도 있습니다.

fun main() {
    val obj: Any = "Hello, Kotlin!"
    val str: String = obj as String // obj가 String 타입이 아니면 예외 발생
    val nullStr: String? = obj as? String // obj가 String 타입이 아니면 null 반환
}

 

 

스마트 캐스팅

스마트 캐스팅(Smart Casting)은 코틀린의 특별한 기능 중 하나로, 변수나 식을 검사하여 그 타입이 확실하다면 명시적인 캐스팅 없이도 해당 타입의 멤버에 접근할 수 있도록 해줍니다.

fun process(obj: Any) {
    if (obj is String) {
        println("길이: ${obj.length}") // String 타입으로 스마트 캐스팅되어 length에 접근 가능
    } else if (obj is Int) {
        println(obj * 2) // Int 타입으로 스마트 캐스팅되어 연산 가능
    }
}

fun main() {
    process("Hello")
    process(42)
}

// 출력
// 길이: 5
// 84

`is` 연산자를 사용해 객체가 특정 클래스 타입에 속하는지 검사되면, 이후 코드 블록 내에서 해당 타입으로 간주되며, 캐스팅 없이도 해당 타입의 멤버에 접근할 수 있게 됩니다.

 

스마트 캐스팅이 되려면 불변이여야 한다.

 

 

 

구조 분해(Destructuring Declarations)


구조 분해은 객체나 배열 등의 데이터 구조를 분해하여 그 내부의 요소를 여러 변수로 쪼개어 할당하는 방법입니다. 코틀린에서는 구조 분해를 통해 여러 변수에 동시에 값을 할당할 수 있습니다. 주로 데이터 클래스의 인스턴스나 배열 등에서 구조 분해를 활용합니다.

data class Person(val name: String, val age: Int)

fun main() {
    val person = Person("준형", 26)
    val (name, age) = person // 데이터 클래스의 속성을 변수에 구조 분해하여 할당

    println("이름: $name 나이: $age") // 출력: 이름: 준형 나이: 26
}

 

구조 분해 선언의 내부에서는 각 변수를 초기화하기 위해 `componentN`이라는 함수를 호출하게 되며, 여기서 N은 구조 분해 선언에 있는 변수 위치에 따라붙는 번호입니다.

 

데이터 클래스의 주 생성자에 들어있는 프로퍼티에 대해서는 컴파일러가 자동으로  `componentN` 함수를 만들어줍니다. 위의 Person 데이터 클래스는 컴파일 과정에서 아래와 함수를 생성합니다.

   @NotNull
   public final String component1() {
      return this.name;
   }

   public final int component2() {
      return this.age;
   }

 

 

그러므로 위의 예시와 같은 구조 분해는 컴파일러가 자동으로 `componentN` 함수로 아래와 같이 호출될 수 있습니다.

val name = person.component1()
val age = person.component2()

 

 

 

 

범위(Range)


범위는 .. 연산자를 사용하여 생성할 수 있습니다. 이 연산자는 시작 값부터 끝 값까지의 연속된 값들을 포함하는 범위를 생성합니다. 범위는 주로 숫자나 문자에 대해 사용됩니다.

val intRange: IntRange = 1..5 // 1, 2, 3, 4, 5
val charRange: CharRange = 'a'..'z' // 'a', 'b', ..., 'z'

 

 

범위를 배열로 변환

toList() 함수나 toTypedArray() 함수를 사용하여 범위를 리스트나 배열로 변환할 수 있습니다.

val intRange: IntRange = 1..5
val intList: List<Int> = intRange.toList()
val intArray: Array<Int> = intList.toTypedArray()

 

 

배열을 범위로 변환

배열을 범위로 변환하려면 배열의 첫 번째 요소와 마지막 요소를 사용하여 .. 연산자를 이용하면 됩니다.

val intArray: Array<Int> = arrayOf(1, 2, 3, 4, 5)
val intRange: IntRange = intArray.first()..intArray.last()

 

 

또는 indices 프로퍼티를 사용하여 배열의 인덱스 범위를 범위로 변환할 수도 있습니다.

val intArray: Array<Int> = arrayOf(1, 2, 3, 4, 5)
val intRange: IntRange = intArray.indices

 

 

 

 

날짜 처리


`java.util.Calendar` 클래스는 Java에서 날짜와 시간을 처리하기 위해 제공되는 클래스입니다. Kotlin에서도 이 클래스를 활용하여 날짜와 시간을 다룰 수 있습니다. 아래는 Calendar 클래스를 사용하여 날짜와 시간을 처리하는 예시입니다.

import java.util.Calendar

fun main() {
    // 현재 날짜와 시간 가져오기
    val calendar = Calendar.getInstance()
    println("현재 날짜와 시간: ${calendar.time}") // 출력: 현재 날짜와 시간: Fri Aug 18 12:29:56 KST 2023
}

 

`java.util.Date` 클래스는 Java 8 이전의 날짜 및 시간 API이며, Java 8 이후의 java.time 패키지나 Kotlin의 kotlinx-datetime 라이브러리를 사용하는 것이 더 권장됩니다. 아래 예시는 `java.util.Date` 클래스를 사용하여 날짜를 포매팅하는 방법을 보여줍니다.

import java.text.SimpleDateFormat
import java.util.Date

fun main() {
    val currentDate = Date()

    val dateFormatter = SimpleDateFormat("yyyy-MM-dd")
    val timeFormatter = SimpleDateFormat("HH:mm:ss")

    val formattedDate = dateFormatter.format(currentDate)
    val formattedTime = timeFormatter.format(currentDate)

    println("Formatted Date: $formattedDate") // 출력: Formatted Date: 2023-08-18
    println("Formatted Time: $formattedTime") // 출력: Formatted Time: 12:31:16
}

 

 

시스템 날짜 처리

`System.currentTimeMillis()` 메서드는 현재 시스템의 시간을 밀리초 단위로 반환하는 메서드입니다. 이를 활용하여 현재 시간을 처리하는 예시를 아래에 제시하겠습니다.

import java.util.Date

fun main() {
    val currentTimeMillis = System.currentTimeMillis()
    println("시스템 시간: $currentTimeMillis") // 출력: 시스템 시간: 1692329696852

    val date = Date(currentTimeMillis)
    println("날짜로 변환: $date") // 출력: 날짜로 변환: Fri Aug 18 12:34:56 KST 2023
}

 

 

단점

다만 이러한 legacy한 유틸리티 클래스들은 단점들이 있습니다. 다음과 같은 표를 보겠습니다.

 

쓰레드 안전성이 보장도 안되고 API도 잘 구성이 되어있지 않고 무엇보다 java.util.Date 는 그저 시간에 대해서 나타낸 것이기 때문에 Timezone 등에 대한 지원이 복잡하고 직관적이지 않습니다.

 

이런것들을 보완해서 만들어진 것이 바로 Java 8의 java.time 패키지의 API 들입니다. 이 내부엔 LocalDateTime , LocalDate , ZonedDateTime 과 같은 유틸리티들입니다. 

 

 

 java.time 패키지

 java.time 패키지에 포함된 `LocalDate`를 사용해서 날짜 정보를 나타낼 수 있습니다.

import java.time.LocalDateTime
import java.time.format.DateTimeFormatter

fun main() {
    val currentDateTime: LocalDateTime = LocalDateTime.now()

    val formatter: DateTimeFormatter = DateTimeFormatter.ISO_DATE
    val formattedDateTime: String = currentDateTime.format(formatter)

    println("현재 날짜 $formattedDateTime") // 출력: 현재 날짜 2023-08-18
}

 

 

타임 존

특정 장소의 날짜와 시간을 가져오고 싶을 때는 타임 존을 가져와서 날짜를 생성합니다.

import java.time.ZoneId
import java.time.ZonedDateTime

fun main() {
    val zoneId: ZoneId = ZoneId.of("Asia/Seoul")
    val zonedDateTime: ZonedDateTime = ZonedDateTime.now(zoneId)

    println("서울의 현재 시간: $zonedDateTime")
    // 출력: 서울의 현재 시간: 2023-08-18T12:42:56.375762500+09:00[Asia/Seoul]
    
    val newYorkZoneId: ZoneId = ZoneId.of("America/New_York")
    val newYorkTime: ZonedDateTime = zonedDateTime.withZoneSameInstant(newYorkZoneId)

    println("뉴욕의 현재 시간: $newYorkTime")
    // 출력: 뉴욕의 현재 시간: 2023-08-17T23:42:56.375762500-04:00[America/New_York]
}
profile

Developing Myself Everyday

@배준형

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!