본문 바로가기
깡샘 코틀린

03 코틀린 시작하기

by 농농씨 2023. 6. 6.

03~05장:둘째마당-코틀린 이해하기

 

03-1 코틀린 언어 소개

코틀린은 자바의 가상머신인 JVM에 기반을 둔 언어라서 코틀린으로 작성한 프로그램을 JVM에서 실행할 수 있다.

 

코틀린 파일명을 클래스명과 다르게 선언해도 된다.

소스파일 최상위의 변수와 함수는 파일명+Kt.class 클래스가 자동으로 생성돼서 거기에 저장된다.

따라서 최상위에 선언한 변수,함수를 자바에서 이용하려면 '자동으로 만들어지는 클래스'를 이용해 접근하면 된다.

이름 달라도 문제 없단 말~

 

코틀린 소스 테스트하기

:프로젝트파일 만들고 코틀린 파일 만들고 main함수 만들고

 코틀린파일 우클릭해서 Run '파일명Kt' 누르기 '^+Shift+R'(맥북기준)

 

 

03-2 변수와 함수

변수 선언하기

val, var 키워드로 변수 선언

val:value -> 초기값 할당되면 바꿀수없는 변수

var:variable -> '' 바꿀수 있는 변수

 

✅ val은 초기값이 할당된 후 바꿀 수 없는 변수

✅ var은 선언 이후 바꿀 수 있는 변수

 

타입 지정과 타입 추론

변수명 뒤에 콜론(:) 넣어서 타입 명시할 수 있음.

대입하는 값 따라 타입 유추되면(타입추론) 생략가능.

val number1: Int = 10
var number2 = 10

 

초깃값 할당

최상위에 선언한 변수나, 클래스의 멤버변수는 선언과 동시에 초깃값 할당해야함

함수 내부에 선언한 변수는 바로 할당하지 않아도 됨.

 

초기화 미루기

lateinit, lazy 키워드 사용

- lateinit 사용 예시

lateinit var data1: Int // Int라서 오류
lateinit val data2: String // String이지만 val 이라서 오류
lateinit var data3: String // 성공

 

 

 

 

 

 

lateinit 규칙2가지

:var 키워드로 선언한 변수에만 사용 가능

:Int, Long, Short, Double, Float, Boolean, Byte 타입에 사용 불가(String은 사용가능)

-lazy 사용예시

변수 선언문 뒤에 by lazy{}형식으로 선언함

소스에서 변수가 최초로 사용되는 순간 중괄호 실행돼서 결괏값이 변수의 초기값으로 저장됨.

중괄호 마지막 줄이 초기값 됨.

 

더보기
https://m.blog.naver.com/iguyo/10154560670#

데이터타입

전제:코틀린의 모든 변수는 '객체'이다. 따라서 코틀린의 모든 타입은 객체 타입이다.

 정수를 다루는 타입이 Int인데 Int는 기초 데이터 타입(primitive type)이 아니라 '클래스'이다.

부연:클래스는 데이터타입을 생성할 수 있음. 데이터 타입은 객체를 생성할 수 있음. 즉, 클래스는 객체를 생성할 수 있음.

   <->기초데이터타입(비객체 타입)은 객체를 생성할 수 없음. 따라서 둘이 대조됨

 

var data: Int = 10

var data2: Int? = Null 

/* 
  Int타입이 기초데이터 타입(=비객체 타입)이라면 (객체를 생성할 수 없으므로)
  변수에 null을 대입할 수 없다. 하지만 코틀린의 모든 타입은 기초 데이터타입이 아닌 객체(객체 타입)이므로 
  null을 대입할 수 있다.
 */

 

 

기초타입객체: 기초 데이터를 객체로 표현하는 타입

:Int, Short, Long, Double, Float, Byte, Boolean

문자와 문자열 표현하는 타입

: Char, String

Char타입의 데이터는 Number타입으로 표현할 수 없다. (숫자만 쓰면 안되고 꼭 따옴표로 감싸야 한단 얘기)

String은 큰따옴표나 삼중따옴표로 감싸서 표현함

큰따옴표에는 \n과 같은 '이스케이프 시퀀스(escape sequence)'로 줄바꿈을 표현함

삼중따옴표는 줄바꿈같은 형식이 코드 그대로 유지됨

 

문자열템플릿(string template)

:String타입의 데이터에 변숫값이나 연산식의 결과값 포함해야 할 때 ${}써줘야함

틀을 만들어놓고 데이터가 들어갈 공간(${ })을 주면 데이터가 알아서 그 틀에 맞게 들어가 문자열이 완성됨. 그래서 이것을 문자열 템플릿이라고 표현함

 

Any: 모든타입기능

 

Unit: 반환문이 없는 함수에서 사용하는 데이터타입

Unit타입으로 선언하면 Unit 객체만 대입할 수 있음반환문이 없음을 명시적으로 나타낼때 주로 씀.

fun some():Unit{
    println(10)
}

fun some(){. //함수 선언시 반환타입 생략하면 자동으로 Unit 적용됨
    println(10)
}

은 동일하다.

 

Nothing:null이나 예외를 반환하는 함수에서 사용하는 데이터타입

nothing으로 선언한 변수에는 null만 대입 가능

주로 함수의 반환 타입에 사용

항상 Null만 반환하거나 예외를 던지는 함수에서 사용

fun some1(): Nothing? { //물음표가 붙는다는 건 null도 허용한단 얘기
	return null
}

fun some2(): Nothing {
	throw Exception()
}

왜 변수타입에 ?가 붙는지 참조: https://2-nan.tistory.com/4

 

널 허용과 불허용

코틀린의 모든 타입은 객체이므로 변수에 null 대입 가능.

변수 선언시 null대입이 가능한 변수인지(널 허용,nullable) 불가능한 변수인지(널 불허용,not null)인지 명확하게 구분해서 사용해야 함

타입 뒤에 물음표(?)를 붙이면 널 허용으로 선언하는 것임. 붙이지 않으면 널 불허용으로 선언임.

 

함수 선언하기:fun 키워드 사용

fun 함수명(매개변수명: 타입): 반환타입 { ... }

fun remove(_ with: Model) { ... }
fun get(_ page: Int) { ... }

반환타입 선언을 생략하면 자동으로 Unit 적용

함수의 매개변수(parameter)에는 var이나 val키워드를 사용할 수 없다. val이 자동으로 적용되며 함수 안에서 매개변숫값을 변경할 수 없다.

// ❌ Error!
fun some(data1: Int): Int {
	data1 = 20 
    return data1
}

// ✅
fun some(data1: Int): Int {
    var data = data1
	data = 10
    return data
}

또한 함수의 매개변수에는 기본값(default value)을 선언할 수 있다.

매개변수에 기본값을 선언했다면 호출할 때 인자(argument)를 전달하지 않아도 되며 알아서 기본값이 적용된다.

fun main(){
	fun some(data: Int, data2: Int = 10): Int { //10에 주목!!
    	return data1 * data10
    }
    println(some(10)) //data2는 자동으로 기본값인 10으로 할당 
    println(some(10,20)) //data2가 입력한 20으로 할당됨
}

//실행결과:
100
200

이때 매개변수명을 생략하고 함수를 호출하면 괄호 속의 인자를 순서대로 할당함.

만약 매개변수명을 지정해서 호출하면 순서를 지키지 않아도 됨.

ex. some(data2=20, data1=10)

 

컬렉션 타입(collection type)

여러 개의 데이터를 표현하는 방법. 

Array, List, Set, Map 이 있음.

 

Array-배열표현

코틀린의 배열은 array 클래스로 표현함.

array클래스의 생성자에서 첫번째 매개변수는 배열의 크기이고, 두번째는 초깃값을 지정하는 함수임.

('클래스'니까 '생성자'라는 말 씀)

배열생성예시
<init>(size:Int, init:(Int)->T)

배열의 타입은 제네릭(generic)으로 표현함

(제너릭이란? 선언하는 곳이 아니라 이용하는 곳에서 타입을 지정하는 기법)

Array<Int>:정수배열

Array<String>:문자열 배열

배열선언 예
val data1: Array<Int> = Array(3, {0})
//Array()생성자의 첫번째 인자가 3이고, 두번째 인자는 0을 반환하는 람다 함수이므로
//0으로 초기화한 데이터를 3개 나열한 정수형 배열을 선언함.

배열의 데이터에 접근할 때는 대괄호[]를 사용해도 되고 set()이나 get()을 사용해도 된다.

배열의 데이터에 접근하는 예
fun main(){
	val data1: Array(3,{0})
    data1[1]=10
    data1[2]=20
    data1.set(2,30) //set함수 사용
    
    println( //삼중괄호 사용함
    	"""
    array size : ${data1.size}
    array data:${data1[0]},${data1[1]},${data1.get(2)}
    """ //get함수 사용하여 2번째 데이터 가져옴
    )
}

 

 

기초타입의 배열

복습:배열의 타입을 Array<Int>처럼 제네릭으로 명시함

이때 배열의 데이터가 기초타입이라면 Array를 사용하지 않고 각 기초 타입의 배열을 나타내는 클래스를 이용할 수도 있다.

즉,

BooleanArray, ByteArray, CharArray, DoubleArray, FloatArray, IntArray, LongArray, ShortArray 클래스를 이용할 수도 있다.

또한 arrayOf()라는 함수를 이용하면 배열을 선언하면서 동시에 값을 할당할 수도 있다.

기초타입 배열 선언 예시
val data1:IntArray = IntArray(3,{0})
val data2:BooleanArray = BooleanArray(3,{false})

배열 선언과 동시에 값 할당 예시
fun main(){
	val data1=arrayOf<Int>(10,20,30) //크기가 3인 Int배열을 선언하고 동시에 10,20,30으로 할당
    println(
    	"""
    array size : ${data1.size}
    array data : ${data1[0]}, ${data1[1]}, ${data1.get(2)}//get함수 사용
    )
}

이때, arrayOf()함수도 기초 타입을 대상으로 하는 booleanArrayOf(), byteArrayOf()등등...함수를 제공한다.

다른 기초타입에서의 arrayOf()함수 활용 예시
val data1 = intArrayOf(10,20,30)
val data2 = booleanArrayOf(true,false,true)

 

 

List,Set,Map

Collection 인터페이스를 타입으로 표현한 클래스들이며, 통털어 컬렉션 타입 클래스라고 한다.

List:순서가 있는 데이터 집합, 데이터 중복 허용

Set:순서 없음, 데이터 중복 불허용

Map:키와 값으로 이루어진 데이터 집합, 순서 없음, 키의 중복은 불허용

 

Collection타입의 클래스는 가변(mutable)클래스와 불변(immutable) 클래스로 나뉨

불변클래스:초기에 데이터 대입하면 변경 불가

가변클래스:이후에도 데이터 추가,변경 가능

ex.List:불변타입, size(),get()제공, add(),set() 미제공(각각 데이터 추가,변경하는 함수)

    MutableList:가변타입, size,get,add,set 모두 제공

Set,MutableSet, Map,MutableMap 도 마찬가지

 

리스트 사용 예시
fun main(){
	var list = listOf<Int>(10,20,30)
    //listOf()함수로 List객체를 만들면서 매개변수에 초기값 대입,<Int>라서 정수리스트임
    println(
    	"""
    list size : ${list.size}
    list data : ${lsit[0]}, ${list[1]}, ${list.get(2)}
    """
    //데이터 얻을때 대괄호로 얻거나 get()함수 이용할 수 있음
    )
}

가변(mutable)리스트 사용 예시
fun main(){
	var mutableList = mutableListOf<Int>(10,20,30)
    mutableList.add(3,40)//add()함수로 데이터 추가
    mutableList.set(0,50)//set()함수로 데이터 변경
    println(
    	"""
    List size : ${mutableList.size}
    List data : ${mutableList[0],${mutableList.get(3)}
    """
}

 

 

Map 객체

키와 값으로 이루어진 데이터의 집합.

Map객체의 키와 값은 Pair객체를 이용할 수도 있고 '키 to 값'형태로 이용할 수도 있다.

집합 사용 예
fun main(){
	var map = mapOf<String, String>(Pair("one", "hello"), "two" to "world")
    //키도 값도 String타입으로 제네릭 타입을 지정함. map을 선언함과 동시에 키와 값 할당함
    //pair(키,값)형태로도 할당하고 '키 to 값' 형태로도 할당함
    println(
    	"""
    map size : ${map.size}
    map data : ${map.get("one")}, ${map.get("two")}//키와 값 형태의 데이터라서 get함수만 사용됨
    """
}

 

 

 

03-3 조건문과 반복문

조건문 if~else

var data=10
if (data>0){//조건에 맞거나
	println("양수")
} else { //조건에 안맞거나
	println("0보다 작거나 같음")
}

else if로 조건 여러개 나열할수도 있음

var data=10
if (data>10){
	println("10보다 큰 양수")
} else if (data>0 && data<=10){ 
	println("10보다 작거나 같은 양수")
} else {
	println("음수")
}

if data < 10 { foo() }
else if data <= 10 { foo2() }

코틀린에서, if~else는 표현식(expression)으로도 사용할 수 있다.

표현식이란? 결괏값을 반환하는 계산식

*if~else문을 표현식으로 사용하려면 항상 else가 있어야 함.(중간에 else if 들어갈 수도 있음)

*각 실행문의 '마지막줄만' 반환됨

// if~else문을 표현식으로 사용한 예
val result = if(조건문) {
	참일때 반환할 내용->마지막줄이 result에 저장됨
} else {
	거짓일 때 반환할 내용->마지막줄이 result에 저장됨
}

 

 

조건문 when

소괄호 안이 조건, 각 값->실행할 구문

조건에 정수 아닌 문자열 등의 다른 타입 데이터 넣을수도있음

조건문 when 사용 예시
fun main(){
	var data = 10
    when(data) {
    	10 -> println("10임")
        20 -> println("20임")
        else -> {
        	println("data is not valid data")
        }
    }
}
when 조건문에서 다양한 유형의 조건 예시
fun main(){
	var data: Any=10
    when(data){
    	is String->println("String") //데이터타입이 조건,is는 타입 확인 연산자!
        20,30->println("20 or 30") //조건 두개 동시에
        in 1..10->println("1~10") //범위를 조건으로 걺,in은 범위 지정 연산자!
        else->println(not valid)
    }
}
when 소괄호()로 데이터 명시하지 않고 조건만 명시한 경우
var data=10
when{
	data <0 ->println("A")
    data >100 ->println("B")
    else ->println("C")
}
실행결과: C
더보기

만약 when 조건이 겹치면?

fun main(){
    var a=101
    when{
        a>100 -> println("A")
        a>10 -> println("B")
        else -> println("C")
    }
}

실행결과:A

처음 조건에 맞는 구문만 반환하고 뒷문장은 확인 안함

when문을 표현식으로 사용한 예시
var data=10
val result = when{//조건에 맞는 값이 변수 result에 저장됨
	data >= 10 ->"10이상"
    else ->"10미만"
}
println(result)

 

반복문 for과 while

for문

: 제어 변숫값을 증감하면서 특정 조건이 참일 때까지 구문을 반복해서 실행함. 이때, 조건은 주로 범위 연산자인 in을 사용함

for~in 반복문 예시
var sum: Int=0 //변수 sum 선언하면서 정수형으로 타입 지정하고 0으로 값 초기화
for (i in 1..10){
	sum += i //1부터 10까지 반복하면서 for문 반복으로 sum에 누적됨
}
println(sum)
실행결과 : 55
다양한 for문 증감조건 예시
for (i in 1..10) {...} //1부터 10까지 1씩 증가
for (i in 1 until 10) {...} //1부터 9까지 1씩 증가
for (i in 2..10 step 2) {...} //2부터 10까지 2씩 증가
for (i in 10 downTo 1) {...} //10부터 1까지 1씩 감소

증감 조건을 숫자로 명시하지 않고 컬렉션 타입의 '데이터 개수만큼' 반복하게 할 수도 있다.

반복 조건에 컬렉션 타입 활용한 예시
var data = arrayOf<Int>(10,20,30)
for (i in data.indices){
	//indices:컬렉션 타입의 인덱스값
	print(data[i])
    if (i !=data.size - 1) print(",")
    //i값이 데이터사이즈-1(=2) 과 다르면 콤마 출력한다(i가 2되기 전까지 콤마 출력하고 2되면 출력안함)
}
실행결과 : 10,20,30(줄바꿈 없음)

만약 for문을 반복하면서 인덱스와 실제데이터를 함께 가져오려면 withIndex()함수를 이용해라.

인덱스와 데이터를 가져오는 withIndex() 함수
var data = arrayOf<Int>(10,20,30)
for ((index, value) in data.withIndex()){
	print(value)
    if (index !==data.size -1) print(",")
}

var data = arrayOf<Int>(10, 20, 30)

for (index, value in data.withIndex()) { 
    print(value)

    if index != (data.size - 1)
         print(",")

}

while문으로도 반복문 작성할 수 있다.

while문은 조건문이 참이면 중괄호{}로 지정한 영역을 반복해서 실행함.

fun main(args: Array<String>) {
	var x=0
    var sum1=0
    while(x<10){//변수가 10보다 작으면 계속 반복해라,
    	sum1 += ++x //x에 1을 더한 값을 저장하고 그 값을 sum1에 더해라(1부터 더해짐)
    } //x=9일때 ++x로 인해 10까지 증가한 후 sum1에 더해짐
    println(sum1)
}
실행결과: 55