반응형

1. 예외처리

  1. 기본 구조는 try-catch-finally이다
  2. 코틀린은 Checked Exception을 지원하지 않는다.(코틀린에서의 예외는 모두 UnChecked Exception)
  3. 코틀린에서는 throws를 통해 예외 선언을 할 필요가 없다
  4. 코틀린의 try-catch블록은 값을 반환할 수 있다
val result: Int = try {
    10 / 2
} catch (e: Exception) {
    -1
}

 

Custom Exception

Custom Exception을 정의 할 때는 것은 Exception이나 RuntimeException 클래스를 상속하면 된다

코틀린에서는 Checked Exception이 없으므로 의미상의 차이로 구분해서 상속을 하면 된다

(비즈니스 로직 내 발생하는 에러는 RuntimeException으로 하는게 의미상 적절해 보인다)

/*
Throwable (클래스)
 ├── Exception (클래스)
 │    ├── RuntimeException
 │    └── IOException, etc.
 └── Error (클래스)
*/

class CustomException(message: String) : Exception(message)

 

runCatching

runCatching을 활용하여 예외를 안전하게 처리할 수 있다

runCatching에 의해 생성된 결과에 따라 분기로 로직을 실행할 수 있다

val result = runCatching { // 코틀린 내장 함수로 성공 또는 예외를 Result<T>로 감싸주는 인라인 함수
    val x = 11 / 0
    x
}

result
    .onSuccess { println("success: $it") }
    .onFailure { println("fail: ${it.message}") }

 

인라인 함수란?

- 함수 호출을 없애고 본문을 직접 복사해서 넣는 고차 함수 최적화

- fun 앞에 inline 키워드를 통해 정의

- 객체 생성이 없고, 호출 오버헤드 없음

반응형
반응형

1. 컬렉션

코틀린에서 컬렉션은 읽기 전용(Immutable)변경 가능(mutable)한 컬렉션으로 나뉜다

 

읽기 전용 컬렉션(Immutable)

Collection<T> (인터페이스)

  • 읽기 전용 컬렉션의 최상위 계층으로 Iterable<T>를 상속 받는다 (Iterable은 순차적으로 반복이 가능한지 정의하는 인터페이스)
// List<T>
val abc = listOf("a", "b", "c")

println("${abc.size}")
println("${abc.get(0)}")
println("${abc[1]}")

// Set<T>
val setA = setOf("a", "b", "c")
val setB = setOf("a", "b", "c")
println("equals: ${setA==setB}") // true

// Map<K,V>
val mapA = mapOf("a" to 1, "b" to 2, "c" to 3)
val mapB = mapOf("a" to 1, "b" to 2, "c" to 3)

println(mapA.get("a"))
println("equals: ${mapA == mapB}") // true

 

변경 가능한 컬렉션(Mutable)

MutableCollection<T> (인터페이스)

  • 변경 가능한 컬렉션의 최상위 계층으로 Iterable<T>를 상속 받는다
// MutableList<T>
val alpabets = mutableListOf("a", "b", "c")
// mutableListOf<String>("a", "b", "c")에서 형식 추론으로 String 생략
alpabets.add("d")
alpabets.add("e")
alpabets.removeAt(1)

println(alpabets)

// MutableSet<T>
val setA = mutableSetOf("a", "b", "c")
val setB = mutableSetOf("a", "b", "c")

println("equals: ${setA==setB}") // true

setA.add("a")
println("equals: ${setA==setB}") // true

setB.add("d")
println("equals: ${setA==setB}") // false

// MutableMap<K,V>
val mapA = mutableMapOf("a" to 1, "b" to 2)
val mapB = mutableMapOf("a" to 1, "b" to 2)

println(mapA.keys)
println(mapB.values)

println("equals: ${mapA == mapB}") // true

mapA["a"] = 9
println("equals: ${mapA == mapB}") // false

mapB.remove("a")
println("equals: ${mapA == mapB}") // false
반응형
반응형

1. Nullable, Non Nullable

코틀린의 변수는 기본적으로 Non Nullable하다

 

하지만 필요에 따라 null을 허용할 수 있다

프로퍼티 선언시 타입의 끝에 ?를 붙여주면 null을 허용할 수 있다

(1)
var name: String? = null
var length: Int? = name?.length

(2)
var name2: String? = "kim"
var length2: Int = name2!!.length

(3)
var name3: String? = null
var length3: Int = name3?.length ?: 0
  1. name이 null을 허용하고 있다 ?(Safe Call Operator)를 통해 name이 null인 경우 null을 반환하고, null이 아닌 경우 name의 length를 반환한다
  2. name2는 null을 허용하지만 “kim”이라는 값이 할당되어 있다
    이때 name2는 non null을 보장하지 않으므로 ??:를 활용해서 사용해야하지만, 값이 있음을 보장할 수 있다면
    !!(Non-null Assertion Operator)를 통해 개발자가 null이 아님을 보장하고 사용할 수 있다
    단, 이 경우 null이면 NullPointerException이 발생한다

  3. ?:(Elvis Operator)를 통해 null인 경우 반환할 값을 지정할 수 있다
반응형
반응형

1. enum

enum class DevelopCategory(val code: String) {
	DEVELOPING("dev") {
		override fun apply() {
			println("develop")
		}
	},
	DEPLOY("dpy") {
		override fun apply() {
			println("deploy")
		}
	},
	PRODUCTION("prd") {
		override fun apply() {
			println("production")
		}
	};
	
	abstract fun apply()
    
    companion object {
        fun fromCode(code: String): DevelopCategory? =
            values().find { it.code == code }
    }
}

상수 값을 저장할 공간을 별도로 선언하지 않아도 되며, 추상 함수가 존재하는 경우 이에 대한 구현을 강제한다

 

enum, when 예제

fun test(category: DevelopCategory) {
	
	when (category) {
		DevelopCategory.DEVELOPING -> println("develop")
		DevelopCategory.DEPLOY -> println("deploy")
		DevelopCategory.PRODUCTION -> println("production")
	}
	
	category.apply()
	
	println("${category.code}")
	println(DevelopCategory.fromCode(category.code))
}

 

반응형
반응형

1. Type Checking, Casting

코틀린은 강타입 언어로 변수의 타입을 정확하게 지정해야 한다

이를 위해 타입 안전성을 보장하기 위한 타입 체크와 타입 캐스팅의 다양한 방법이 존재한다

is

  • 객체가 특정 타입인지 확인하는데 사용
  • is를 통한 검사에서 타입이 맞다면, 자동으로 그 타입으로 간주(smart casting)

as

  • 명시적 캐스팅으로 타입이 맞지 않으면 예외를 던진다

as?

  • 안전한 캐스팅으로 타입이 맞지 않으면 null을 반환한다

타임 체크 및 캐스팅에 대한 예제

fun test(obj: Any) {
    
    if (obj is String) {
	    print("String 임!")
    } else {
    
	    val t1 = obj as? Int
	    if (t1 != null) {
		    print("Int 임!")
	    }
	    
	    when (obj) {
		    is Person -> println("Person 임!"
			  else -> println("else 임!")
	    }
	    
	    val t2 = obj as Double // 캐스팅 실패시 exception 던짐
    }
}

 

반응형
반응형

1. 접근 지정자

코틀린의 접근지정자를 통해 가시성을 적용할 수 있다

 

1. public

  • 코틀린의 기본 접근지정자로 함수나 프로퍼티 등에 접근지정자를 쓰지 않으면 기본으로 public으로 적용된다
  • 어디서든지 접근 가능한 가시성

2. private

  • 클래스 내부에서만 접근 가능한 가시성
  • 파일 레벨에서는 같은 파일 내에서만 접근

3. internal

  • 같은 모듈 내에서만 접근이 가능한 가시성

4. protected

  • 상속받은 클래스에서만 접근 가능한 가시성
  • 파일 레벨에서는 사용 불가능
class Person {
	var a = "a"
	public var b = "b"
	private var c = "c"
	internal var d = "d"
	protected var e = "e"
	
	fun a(): String {
		return "a"
	}
	
	public b() {
		println("b")
	}
	
	private c(): Int {
		return 1
	}
	
	internal d(p1: String) {
		println("${p1}")
	}
	
	protected e(p1: Int, p2: String): String {
		return p2.repeat(p1)
	}
}
반응형
반응형

1. interface

interface Person {
	
	// (1)
	var age: Int
	
	// (2)
	val height: Int
			get() {
				return 30
			}
	
	// (3)
	fun earn(): Int
	
	// (4)
	fun talk() = println("말한다!")
	
	// (5)
	fun walk() {
		println("걷는다")
	}
	
	// (5)
	fun greet(): String {
		return "안녕하세요"
  }
  
  @JvmStatic
  fun hello() = println("안녕!")
  
  companion object {
	  fun hello_en() = println("hello!")
  }
}

코틀린의 인터페이스는 자바 유사하나 조금 차이가 있다

 

1. 추상 프로퍼티 선언 가능
    - var를 선언하는 경우 getter, setter를 반드시 구현해야한다(기본 구현 필수)

    - 계산 프로퍼티

    - 백킹 필드(backing field)가 존재하지 않음

    - 추상 프로퍼티에 대한 default 구현 가능 get()

 

2. static이 없어 companion object 또는 @JvmStatic 어노테이션으로 가능

3. default 대신 일반적인 함수 선언과 동일하게 선언 하고 몸체(body)가 있는 경우 default 함수로 판단

 

* 인터페이스 다중 상속 시 동일한 시그니처에 대한 처리는 명시적으로 super 호출 함으로서 해결 할 수 있다

interface A {
    fun run() = println("A Run")
}

interface B {
    fun run() = println("B Run")
}

class C : A, B {
    override fun run() {
        super<A>.run()
    }
}

 

 

 

반응형
반응형

1. object

object 키워드는 코틀린에서 클래스를 정의하면서 동시에 객체를 생성하는 키워드 이다

class 자리에 object 키워드를 사용하면 된다

 

1) 객체 선언

  • class 대신 object 키워드를 사용하면 클래스를 선언함과 동시에 객체를 생성하게되고 이는 싱글톤 객체가 된다
object Counter {
	private var count: Int = 0
	
	fun plus() {
		count++
	}
	
	fun getCount() = count
}

fun main() {
	println(Counter.getCount())
	Counter.plus()
	Counter.plus()
	println(Counter.getCount())
}

 

2) 객체 식

  • 익명클래스를 통해 객체를 생성할 때 사용
interface Person {
	fun work()
}

fun main() {
	val worker = object: Person {
		override fun work() {
			println("일한다!")
		}
	}
	
	worker.work()
	
}

 

3) 동반 객체(companion)

  • 클래스의 객체들이 공유해야하는 메소드나 프로퍼티를 정의할 때 사용
  • Factory Method 패턴 사용시 활용 가능
class User private constructor(val id: Int) {
    companion object {
        fun create(id: Int): User {
            return User(id)
        }
    }
}

fun main() {
    val user = User.create(1111)
    println(user.id)  // 1111
}

마치 자바의 static 키워드를 사용하는 것과 비슷한 효과가 나온다(코틀린에는 static이 존재하지 않는다)

컴패니언(companion) 객체는 최종적으로 jvm 바이트코드로 컴파일(.class) 될 때 static 멤버로 컴파일 된다

반응형

+ Recent posts