변수 선언

// Top-Level에 위치한 변수
var x = 6

fun main(){
	x+=1

// 키워드, 변수명, 타입, 값 순서로 작성 
	val a : Int = 1
// 타입 생략 가능 코틀린 컴파일러가 값을 보고 타입을 추론해줌 
	val b = 2
// 지연 할당 가능 단 타입을 명시해 주어야 함 
	val c : Int
	c = 3
// 컴파일 오류 
	val d
	d = 4
// val(value) 재할당 불가능 (상수)
// var(variable) 재할당 가능 (변수)
	var e : String = "Hello World"
	e = "World"

// var에서 타입이 고정되면 타입은 변경이 불가능	
	var f = 5
	f = "Hello"
}

문자열 처리

fun main() {
	val version = "1.3.50"
    val javaStyle = "Hello, kotlin" + version + "!"
    val kotlinStyle = "Hello, Kotlin ${version}!"
    println(javaStyle)
    println(kotlinStyle)
    
    val num = 10
    println("val num is equal to 10:${num == 10}.")
    println("""\$""")
    println("\$")
    println("""
    	|hello
        |my name is kotlin.
    """.trimMargin())
}

함수 선언

fun sum(a: Int, b: Int) : Int {
	return a + b
}

// 표현식 스타일
fun sum2(a: Int, b: Int) : Int = a + b

// 표현식 & 변환타입 생략
fun sum3(a: Int, b: Int) = a + b

// 몸통이 있는 함수는 반환 타입을 제거하면 컴파일 오류
fun sum4(a: Int, b: Int){
	return a + b
}

// 반환타입이 없는 함수는 Unit을 반환한다
fun printSum(a: Int, b: Int): Unit{
	println("$a + $b = ${a+b}")
}

// 디폴트 파라미터
fun greeting(message: String = "안녕하세요!!"){
	println(message)
}

// named argument
fun log(level: String = "INFO", message: String){
	println("[$level]$message")
}

fun main(){

}

흐름제어

/**
 * You can edit, run, and share this code.
 * play.kotlinlang.org
 */fun main() {

// if else 사용val job = "Software Developer"

    if(job == "Software Developer"){
        println("개발자")
    }else{
        println("개발자아님")
    }

// 코틀린의 if-else는 표현식 값을 리턴하는 것이 가능val age : Int = 10

    var str = if(age>10){
        "성인"
    }else{
        "아이"
    }

// 코틀린은 삼항 연산자가 없다. if else가 표현식이므로 불필요하다.val a = 1
    val b = 2
    val c = if(b>a) b else a

    val day = 2

// 자바의 switch문val result = when(day){
        1 -> "월요일"
        2 -> "화요일"
        3 -> "수요일"
        4 -> "목요일"
        else -> "기타"
    }
    println(result)

// 여러개의 조건을 콤마로 구분해 한줄에서 정의할 수 있다.
	when(getNumber()){
		0, 1 -> print("0 또는 1")
		else -> print("0 또는 1이 아님")
	}

// 범위 연산자 .. 을 사용해 for loop 돌리기 <
	for(i in 0 .. 3 ){
		println(i)	
	} 

// until 을 사용해 반복한다
// 뒤에 온 숫자는 포함하지 않는다 <=
	for (i in 0 until 3){
		println(i)
	}
	
// step 에 들어온 값 만큼 증가시킨다
	for (i in 0 .. 6 step 2){
		println(i)
	}

// downTo를 사용해 반복하면서 값을 감소시킨다
	for (i in 3 downTo 1){
		println(i)
	}
    
	(10 downTo 1).forEach{
        print("$it ")
    }
    println("")
// 전달받은 배열을 반복
	val numbers = arrayOf(1,2,3)

	for(i in numbers){
		println(i)	
	}

// 자바의 while문과 동일
// 조건을 확인하고 참이면 코드 블록을 실행한 후 다시 조건을 확인
	var x = 5

	while(x>0){
		println(x)
		x--
	}
}

타입체크

fun main() {
	fun test(obj: Any){
        if (obj is Int)println("obj is Int")
        if (obj is String) println("obj is String")
        // obj가 String 타입이 아니라면 공백으로 
        println("print obj as string> ${(obj as? String?:"")}")
    }
    test(1)
    test("Strings")
}

 

널 안정성 (NPE)

fun main(){
    // (컴파일 에러)
    // val a : String = null
    var b : String = "abcdef"
    // (컴파일 에러)
    // b = null

    // Nullable 한 타입임을 명시
    var c : String? = null
    // 해당 값이 null이 아니라면 실행
    println(c?.length)

    val d: Int = if(c!=null) c.length else 0
    println(d)

    // 엘비스 연산자
		// ?:의 왼쪽 객체가 non-null이면 그 객체의 값이 리턴되고, null이라면 ?:의 오른쪽 값을 리턴합니다.
    val e = c?.length?:0 // 해당 타입은 null 이기 때문에 0 리턴
    println(e)

    val f: String? = null

    // !! null이 발생하지 않는다고 컴파일러에게 알리는 단언자
    // null을 없애주지는 않는다.
    val g = f!!.length
}

예외처리

import java.lang.Exception
import java.lang.IllegalArgumentException

fun main(){
    // 코틀린은 checkedException을 강제하지 않음
    Thread.sleep(1)
    // 그래도 try - catch가 필요한 경우 사용가능
    try{
        Thread.sleep(1)
        throw Exception()
    }catch (e: Exception){
        println("경고")
    } finally {
        println("finally 실행!")
    }

    // try - catch 문 또한 값을 반환하는 것 이 가능
    val a = try{
        "1234".toInt()
    }catch (e: Exception){
        println("예외 발생")
    }
    println(a)

    //throw Exception("예외 발생!")
    val b: String? = null
    // Nothing 타입과 ?: 엘비스 연산자를 같이 사용하는 경우는 null이 나올 수 없음
    val c: String = b?:failFast("예외 발생!")

    println(c.length)
    println("앞서 Nothing 타입이 반환된다면 이후 함수는 작동을 보장할 수 없음")
}

// throw를 return 하게 된다면 Nothing 타입을 자동으로 반환 함
fun failFast(message: String): Nothing{
    throw IllegalArgumentException(message)
}

Class

// 특정 애너테이션과 같이 쓸 경우 해당 키워드 사용 권장
class Drink constructor(val name:String){

}
// 기본 생성자에 constructor 키워드 생략가능
class Coffee (
    // 코드 리뷰시 가독성을 위해 후행 ,를 항상 붙일 것을 권장
    var name:String = "",
    var price: Int = 0,
){
    val brand: String
        // 커스텀 getter
        get() = "스타벅스"

    val origin: String
        // 커스텀 getter2
        get() {
            return "콩고"
        }

    var quantity : Int = 0
        // 커스텀 setter
        set(value){
            if(value > 0){ // 수량이 0 이상일 경우에만
                // quantity = value 로 직접 할당하지 않는 이유
                // quantity가 다시 set을 호출하는 무한 재귀 현상이 일어나기 때문
                field = value
            }
        }
}

class EmptyClass
fun main(){
    // getter setter 코드를 따로 작성해주지 않아도 컴파일러가 자동으로 생성 후 실행
    val coffee = Coffee()
    // 보이지 않는 setter 함수 실행
    coffee.name = "아이스 아메리카노"
    coffee.price = 2000
    coffee.quantity = 5
    // 보이지 않는 getter 함수 실행
    println("${coffee.brand} ${coffee.name} 가격은 ${coffee.price}")
}

상속

  • Kotlin의 모든 클래스의 조상은 Any class
open class Dog{
    open var age: Int = 0

    open fun bark(){
        println("walwal")
    }
}

// 코틀린에서 기본생성자 사용시 프로퍼티를 좀 더 쉽게 재정의 가능
// 상속을 받아 구현한 클래스는 프로퍼티나 함수가 자동으로 open 상태가 됨
// 이를 막고자 한다면 final 키워드를 사용해야 함
open class Bulldog(final override var age : Int = 0) : Dog(){

    override fun bark(){
        // 상위 클래스의 함수 호출시
        super.bark()
        println("kungkung")
    }
}

class ChildBullDog():Bulldog(){
    //override var age: Int = 0
    override fun bark(){
        super.bark()
    }
}

// 추상 클래스
abstract class Developer{
    abstract  var age: Int
    abstract  fun code(language: String)
}
// 추상 클래스 상속시 하위 클래스에서 abstract 키워드가 붙은 것을 구현해야 함
class BackendDeveloper(override var age: Int):Developer(){
    override fun code(language: String){
        println("I code with $language")
    }
}

fun main(){
    val dog = Bulldog(age = 2)
    println(dog.age)
    dog.bark()

    val backendDeveloper = BackendDeveloper(age=28)
    println(backendDeveloper.age)
    backendDeveloper.code("Kotlin")
}

인터페이스

  • 하나 이상의 인터페이스 구현이 가능
class Product(val name:String, val price:Int)

interface Wheel{
    // 추상 함수
    fun roll()

}
// 코틀린에서는 인터페이스 에서도 프로퍼티가 존재 가능
interface Cart:Wheel{

    var coin: Int
    // 인터페이스 경우에는 클래스와 다르게 backing field를 사용하면 컴파일 오류 발생
    val weight : String
        get() = "20KG"

    fun add(product: Product)
    // 디폴트 함수
    fun rent(){
        if(coin>0){
            println("카트를 대여합니다")
        }
    }

    override fun roll() {
        println("카트가 굴러갑니다")
    }

    fun printId() = println("1234")
}

// 복수개의 인터페이스에서 같은 이름의 함수가 있는 경우 호출이 안되는 문제점 존재
interface Order{
    fun add(product: Product){
        println("${product.name} 주문이 완료되었습니다")
    }

    fun printId() = println("5678")
}

// 상속과 달리 : 생성자가 아니라 인터페이스 명을 적어준다.
class MyCart(override var coin: Int): Cart,Order{

    override fun add(product: Product) {
        if(coin<=0) println("코인을 넣어주세요")
        else println("${product.name}이(가) 카트에 추가됐습니다")

        // 주문하기 super<인터페이스>를 통해 접근 하는 것으로 해결 가능
        super<Order>.add(product)
    }

    // 두 개의 인터페이스에서 동일한 시그니처의 디폴트 함수를 제공한 상황에서는
    // 각각을 override 해서 재정의 할 필요가 있다
    override fun printId() {
        super<Cart>.printId()
        super<Order>.printId()
    }

}

fun main(){
    val cart = MyCart(coin = 100)
    cart.rent()
    cart.roll()
    cart.printId()
    cart.add(Product(name="장난감",price = 1000))
}

열거형

  • 서로 연관된 상수들의 집합을 enum 클래스를 사용하여 표현
  • 동등성 비교에 자주 이용
enum class PaymentStatus(val label:String):Payable{
    UNPAID("미지급"){
        override fun isPayable(): Boolean = true
    },
    PAID("지급완료"){
        override fun isPayable(): Boolean = false
    },
    FAILED("지급실패") {
        override fun isPayable(): Boolean = false
    },
    REFUNDED("환불") {
        override fun isPayable(): Boolean = false
    };
    // 내부에서 추상 메서드 선언 후 구현 하는 것도 가능
    // abstract fun isPayable(): Boolean
}

// 분리 작업
interface Payable{
    fun isPayable(): Boolean
}

fun main(){

    if(PaymentStatus.UNPAID.isPayable()){
        println("결제 가능 상태")
    }

    // 인자로 들어온 String 을 가지고 일치하는 값을 가지고와 인스턴스 화
    val paymentStatus = PaymentStatus.valueOf("PAID")
    println(paymentStatus.label)

    // 동등성 비교
    if(paymentStatus==PaymentStatus.PAID){
        println("결제 완료 상태")
    }
    
    for(status in PaymentStatus.values()){
        println("[$status](${status.label})")
    }
    // String, 쌍, 순서
    for(status in PaymentStatus.values()){
        println("[${status.name}](${status.label}) : ${status.ordinal}")
    }
}

'Development > Kotlin&Android' 카테고리의 다른 글

Kotlin 문법 Part 4  (0) 2023.03.23
Kotlin 문법 Part 3  (0) 2023.03.16
Kotlin 문법 Part 2  (0) 2023.03.02
스프링의 코틀린 지원  (0) 2023.02.27
Java와 Kotlin의 차이  (0) 2023.02.27

+ Recent posts