본문 바로가기

Study/Kotlin

Kotlin / 내장 자료형 알아보기

반응형

1. 내장 자료형 알아보기

클래스를 만들 때는 항상 기본적인 최상위 클래스가 필요하다.

코틀린에서는 최상위 클래스로 Any 클래스를 제공한다. 이렇게 코틀린이 기본으로 제공하는 클래스를 내장 자료형이라고 한다.

숫자 클래스

코틀린에서 숫자클래스의 상위클래스를 Number로 정의했다.

- NaN (not a number)

실수는 숫자를 정확히 모를 때는 무한대를 사용하고, 숫자가 아닌 경우에는 NaN을 사용한다.

NaN은 계산도 비교도 안된다. 어떤 숫자와 계산해도 항상 NaN이다. (자기 자신과도 비교 x)

 // NaN과의 연산은 항상 NaN
 println(Double.NaN + 100.0) // NaN
 
 // NaN은 비교할 수 없다
 println(Double.NaN == Double.NaN) // false

NaN == NaN은 false이어서 NaN을 판단하려면 isNan()함수를 사용해야함

 

문자와 문자열 자료형

문자열은 기본적으로 변경할 수 없는 객체이다. 

문자열을 변경하려면 StringBuilder 클래스를 사용해야 한다.

코틀린은 문자 클래스인 Char, 문자열 클래스인 String을 제공한다.

 

val myString = "문자열 인덱스 조회"
val item = myString[2]

println(myString.first()) // 문
println(myString.last()) // 회
println(item) // 열
println(myString.getOrNull(myString.length)) // null

 

문자열은 문자를 원소로 갖으므로 내부의 원소를 조회할 수 있다. 

 

빈 문자열 처리

  • 메서드 isEmpty : 빈 문자열 체크, 이스케이프 문자도 문자로 인식
  • 메서드 isBlank : 빈 문자열 체크, 이스케이프 문자는 문자로 인식하지 않는다.
  • 메서드 trimEnd : 마지막 공백만 제거한다.
  • 메서드 trimStart : 처음 공백만 제거한다.
  • 메서드 trim : 공백과 이스케이프 문자를 모두 제거한다.

 

문자열 비교와 대소문자 등의 메서드

  • 메서드 replaceFirstChar : 문자열의 첫 글자를 대문자/소문자로 변경
  • 메서드 uppercase / lowercase : 문자열을 대문자/소문자로 변경
  • 연산자 + : 문자열을 결합
  • 메서드 to : 두 문자열로 튜플을 구성
  • 메서드 compareTo : 영어 대소문자를 구분하지 않고 비교하며 동일한 알파벳을 확인한다.

문자 필터링 처리

문자열도 여러 원소를 가지므로 filter 등 내부 순환을 제공한다. 

메서드 filter는 내부 순환을 돌면서 람다표현식이 실행한 결과가 참인 경우만 추출한다.

val s = "TODAY IS A sunny day"
val res = s.filter{e -> e.isLowerCase()}
println("result : $res")

//result : sunnyday

 

문자열 주요 메서드

  • 메서드 replace : 매칭되는 문자를 변경해서 새로운 문자열을 반환한다.
  • 메서드 contains : 매칭되는 문자열 포함 여부를 확인한다.
  • 메서드 split : 매칭되는 문자열을 기준으로 분리하여 리스트로 반환한다.
  • 메서드 join : 리스트 내의 문자열을 특정 문자 기준으로 결합한다.
  • 메서드 subsequence / substring / slice : 부분 문자열을 만들어 새로운 문자열 객체로 반환한다.
val s1 = "Today is a sunny day, happy"
println("replace : ${s1.replace("sunny","rainy")}")
println("contains : ${s1.contains("Today")}")
val s2 = "독수리, 매, 올빼미, 까치"
val birds = s2.split(",")
println("split : $birds")
println("joinToString : ${birds.joinToString(",")}")

//replace : Today is a rainy day, happy
//contains : true
//split : [독수리,  매,  올빼미,  까치]
//joinToString : 독수리, 매, 올빼미, 까치

 

변경 가능한 문자열 StringBuilder 처리

  • 메서드 append : 마지막 인덱스 뒤에 추가
  • 메서드 insert : 특정 인덱스 위치에 추가
  • 메서드 deleteCharAt : 특정 인덱스 위치의 문자 삭제
  • 메서드 delete: 시작과 종료 인덱스 사잉의 문자 삭제
  • 메서드 clear : 전체 삭제
  • 메서드 setCharAt : 특정 위치의 문자 변경

Any, Unit, Nothing 클래스

Any 클래스 : 공통 메서드 작성

클래스를 정의할 때 아무것도 상속하지 않아도 Any 클래스는 자동으로 상속한다. 

 

Unit 클래스 : 함수는 항상 반환값을 처리하는데, 보통 반환값이 없다는 것은 Unit의 객체를 반환하는 것이다.

println 함수도 출력 이후에 Unit 클래스의 객체를 반환한다. 

 

Nothing 클래스 : 함수를 반환할 때 아무것도 없다는 것을 표시하는 클래스

 

배열

여러 원소를 관리하는 클래스를 컬렉션(collection)이라고 한다.

모든 원소는 하나의 자료형 객체이고, 고정 길이만 만들 수 있다. 배열은 변경 가능한 객체이다.

 

배열 생성과 주요 메서드 처리

val arInt = arrayOf(1,2,3,4)
println("배열의 크기 = ${arInt.size}") // 배열의 크기 = 4
println("배열의 원소의 합  = ${arInt.sum()}") // 배열의 원소의 합  = 10
println("배열의 최대값 = ${arInt.maxOrNull()}") // 배열의 최대값 = 4
println("배열의 최소값 = ${arInt.minOrNull()}") // 배열의 최소값 = 1
println("배열의 평균 = ${arInt.average()}") // 배열의 평균 = 2.5
println("배열의 개수 = ${arInt.count()}") // 배열의 개수 = 4

 

배열의 원소 출력

for (i in arInt) {
    print("$i, ")
}

// 1, 2, 3, 4,

배열의 원소를 출력하기 위해 for 순환문을 사용했다.

 

val arStr = arrayOf("코틀린", "자바")
val x = arStr.indices.iterator() // 반복자로 변환
while (x.hasNext()) { // 원소가 있는지 확인
    println(arStr.get(x.next()))
}
arStr.forEach { println(it) } // 내부 반복자로 처리

//코틀린
//자바
//코틀린
//자바

반복자를 iterator 메서드를 사용해서 생성하고 next 메서드로 원소를 하나씩 읽는다.

 

 

배열의 정렬

배열은 인덱스 순서대로 값을 저장한다. 값을 기준으로 정렬을 할 수 있다.

val arr4 = arrayOf(3,4,2,7,8,1) // 배열 객체 생성

arr4.sort() // 내부 변경 정렬
println(arr4.contentToString())

arr4.reverse() // 내부 변경 역정렬
println(arr4.contentToString())

// [1, 2, 3, 4, 7, 8]
// [8, 7, 4, 3, 2, 1]

배열을 정렬하여 내부 원소의 위치를 바꿀 때는 sort나 reverse 메서드를 사용한다.

 

val arr4 = arrayOf(3,4,2,7,8,1)
val arr5 = arr4.copyOf() // 복사하기

val arr6 = arr5.sorted()
println(arr6)

val arr7 = arr5.sortedDescending()
println(arr7)

val arr8 = arr5.reversed()
println(arr8)

//[1, 2, 3, 4, 7, 8]
//[8, 7, 4, 3, 2, 1]
//[1, 8, 7, 2, 4, 3]

배열의 원소를 정렬하지만 새로운 배열을 만들 때는 sorted, reversed, sortedDescending 메서드를 사용한다.

 

2. 자료형 처리 알아보기

널러블 여부

기본 자료형에 물음표(?)를 붙이면 널을 처리할 수 있는 자료형이 만들어진다.

  • 안전 연산자(?.) : 널 값이 들어오면 널로 처리하고 아니면 뒤에 오는 속성이나 메서드를 실행한다
  • 엘비스 연산자(Elvis ?:) : 널 값이면 다음에 들어오는 값으로 변환한다. 
  • 널 단언 연산자(!!) : 널 값이 안들어온다고 확신할 경우만 사용한다. 널 값이 들어오면 예외를 발생시킴

타입변환

타입 체크

fun checkDataType(value: Any){
    if(value is String){
        println("$value 은/는 문자열 ")
    }
    if(value !is String){
        println("$value 은/는 문자열 아님")
    }
    if(value is Int){
        println("$value 은/는 정수")
    }
    if(value !is Int){
        println("$value 은/는 정수 아님")
    }
}

checkDataType(100)
checkDataType("문자열")
//100 은/는 문자열 아님
//100 은/는 정수
//문자열 은/는 문자열
//문자열 은/는 정수 아님

is와 !is 연산자로 문자열과 정수에 대한 타입을 확인할 수 있다.

 

 

스마트 캐스팅

코드에서 명시적으로 캐스팅을 선언하지 않아도 자동으로 캐스팅되는 것을 의미한다. (명확한 값이 있어야 자료형을 확인할 수 있다.)

if 안에서 is로 검사하면 smart cast가 되는데 그게 가능한 조건과 불가능한 조건은?

-> 불변해야된다. 변할 수 있으면 접근하는 당시에 값이 맞는지 보장할 수 없다.

 

명시적인 자료형 변환

널러블 자료형 등을 가지고 명시적인 자료형을 as, as?로 변환한다.

var x1: String = "100"
var y1: String? ="3000"
x1 = y1 as String
println(x1) // 3000

널러블 문자열을 일반 문자열로 변환했다.

 

 as? String과 as String? 의 차이는?

=> 둘 다 nullable String이 반환되는데, 이 두가지의 차이는...?

as? String : String으로 캐스팅하는데, 실패하면 (안전한 캐스팅 가능)

 

구조분해(Destructing Declaration)알아보기

코틀린에서는 원소를 쉽게 분해해서 변수에 할당할 수 있도록 구조분해를 지원한다.

  • componentN : 구조분해가 가능한 것은 저장된 원소를 위치에 따라 조회할 수 있는 메서드가 자동으로 만들어진다.
  • 구조분해가 가능한 클래스는 내부적으로 component 메서드가 자동으로 생성된다. 이 메서드가 있으면 구조분해를 바로 처리할 수 있다. 이 메서드의 숫자는 실제 원소의 위치이다.
  • data class는 컬렉션이 아니지만, 내부적으로 구조분해를 지원한다.
val a = 100
val b = 200
val c = 300

val st = Triple(a, b, c) // 원소가 3개인 튜플로 구조화
val (a_, b_, c_) = st // 튜플 원소를 변수에 할당 : 구조분해

println(" $a_, $b_, $c_ ")
println(" ${st.first},${st.second},${st.third}")
println(" ${st.component1()},${st.component2()},${st.component3()}")

// 100, 200, 300
// 100,200,300
// 100,200,300

3개의 원소를 갖는 Triple 클래스의 객체를 만든 후, 3개의 변수에 할당하면 3개의 원소가 자동으로 변수에 할당되는데, 이런 처리가 구조분해이다.

튜플 내의 구조분해 속성을 확인하는 first, second, third가 있다. 이 속성으로 조회하면 튜플 내의 원소를 조회할 수 있다.

구조분해를 해서 변수에 할당하면 componentN 메서드가 실행되는 것을 볼 수 있다.

 

3. 범위 알아보기

범위 생성

범위는 시작과 끝에 대한 정보를 속성으로 가진다. 

  • rangeTo 메서드는 범위 연산자와 같은 결과를 만든다.
  • 간격(step) : 이 간격만큼 단계를 넘어서 순환처리
  • 마지막을 포함하지 않으려면 until 메서드로 범위를 만듦
  • downTo 메서드 : 역방향으로 표시할 때는 연산자로 처리할 수 없으므로 반드시 이 메서드를 사용해야 한다.
  • 문자열도 범위를 지정할 수 있지만, 순환은 할 수 없다.

 

4. 날짜(Date) 알아보기

코틀린에서는 자바의 달력(Calendar)을 import하고 날짜를 getInstance를 실행해 가져온다.

val today = Calendar.getInstance()
println("Year : ${today.get(Calendar.YEAR)}")

//Year : 2023

 

가장 간단한 날짜 포매팅은 SimpleDataFormat을 사용한다.

val current = Date()
var formatted = SimpleDateFormat("yyyy-MM-dd")
println("Current : ${formatted.format(current)}")

//Current : 2023-08-20

Date는 Thread safe 하지 않는다. 비추비추

 

지역 날짜 : LocalDateTime을 제공하고, now로 현재 날짜를 가져온다.

val currentDateTime = LocalDateTime.now()
println(currentDateTime)
//2023-08-20T17:35:56.091098