- Today
- Total
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- KotlinInAction
- 일본어기초
- webflux
- 책리뷰
- suspend
- 진짜일본어
- posting
- GIT
- coroutine
- github
- ai
- pullrequest
- errorhandling
- androidstudio
- 진짜학습지후기
- 진짜학습지
- blog
- CustomTab
- Kotlin
- 책추천
- rxjava
- 안드로이드
- PR
- jlpt
- 인공지능
- n3문법
- 코틀린
- Android
- 일본어문법
- 학습지
코딩하는 개굴이
[Kotlin] Kotlin의 기본 한번에 요약하기 본문
해당 내용은 fast campus 의 “실무 프로젝트로 배우는 Kotlin & Spring : 리팩토링부터 서비스 구현까지” 강의를 기반으로 작성되었음을 알립니다.
변수
- 탑레벨 즉, 파일 최상단에 변수를 위치할 수 있음
- 간단한 로직의 경우 굳이 클래스를 생성하지 않고 변수 할당만으로 처리 할 수 있음
var LOG_TAG = "LOG"
fun main() {
//...
}
함수
//기본
fun sum(a: Int, b: Int): Int {
return a + b
}
//표현식 스타일
fun sum(a: Int, b: Int): Int = a + b
//표현식 & 반환타입 생략
fun sum(a: Int, b: Int) = a + b
//몸통이 있는 함수는 반환타입을 제거하면 컴파일 오류 발생
/*
fun sum(a: Int, b: Int) { //이러면 컴파일 오류가 발생. :Int 추가 필요
return a + b
}
*/
//반환 타입이 없으면 Unit 을 반환
fun sum(a: Int, b: Int) {
println("${a + b}")
}
//디폴트 파라미터
fun sum(a: Int, b: Int = 0) {
println("${a + b}")
}
//네임드 아규먼트
fun sum(a: Int = 0, b: Int = 0) {
println("$a + $b = ${a + b}")
}
fun main() {
sum(a=1) //1 + 0 = 1
sum(b=2) //0 + 2 = 2
sum(b=2, a=1) //1 + 2 = 3
sum(1,2) //1 + 2 = 3
sum(a=1, b=2) //1 + 2 = 3
}
흐름제어
if else 는 표현식이라서, 아래와 같이 복잡한 삼항 연산자를 사용하지 않고도 대체하여 사용할 수 있다.
val result = if(b > a) b else a
val resultForWhen = when(type) {
WEB -> "this is web"
MOBILE, AND, IOS -> "this is mobile"
WINDOW -> "this is window"
else -> "this it others"
}
val resultForType = when(getType()) { //type 이 명확할 경우 else 의 생략 가능
HUMAN -> "this is human"
NOTHUMAN -> "this is not human"
}
fun getType():Type = Type.HUMAN
enum class Type {
HUMAN, NOTHUMAN
}
반복문
while 의 경우 Java 와 동일
for(i in 0..3) {} //0,1,2,3
for(i in 0 until 3) {} //0,1,2
for(i in 0..6 step 2) {} //0,2,4,6
for(i in 3 downTo 1) {} //3,2,1
val numbers = arrayOf(1,2,3)
for(i in numbers) {} //1,2,3
NullSafty
컴파일 오류로 미연에 Nullable 이 아닌 타입에 null 을 재할당하는 것을 방지할 수 있음.
Nullable 한 타입을 따로 제공하고 있기 때문에, 해당 타입만 null 을 할당할 수 있다.
//Nullable 의 접근은, 안전 연산자를 이용해 사용 가능하다. 만일 null 이면 뒤의 로직이 수행되지 않는다.
var a: String? = null
println(a?.length)
//엘비스 연산자를 이용해 null 일 경우의 결과 처리
fun getLengthIfNotNull(str: String?) = str?.length ?: 0
예외 처리
코틀린의 모든 예외 클래스는 최상위 예외 클래스인 Throwable 을 상속한다.
- Throwable
- Error
- UncheckedErrors: outofmemory 등 시스템에 비정상적인 상황이 발생하여 예측이 어려운 에러
- Exception
- UncheckedExceptions
- RuntimeException: NullPointerException, ArrayIndexOutofBounds etc.
- CheckedExceptions: FileNotFound 와 같이 try catch 로 처리되어야하는 에러
- UncheckedExceptions
- Error
Kotlin 은 기본적으로는 checkedException 을 강제하지는 않고, 직접 try catch 를 사용할 수 있다.
또한, Kotlin 은 try catch 또한 표현식으로 값을 정의할 수 있다.
throw 를 이용해 Exception 을 던질 수도 있다.
만일 함수에서 throw 를 반환하게 되어 정상적인 동작을 보장하지 않는 경우 해당 함수는 Nothing 타입을 반환하게 된다.
- Nothing 타입의 반환을 elvis 연산자와 함께 사용하면 nullable 값이 null 이 불가한 타입으로 변경된다.
val a = try {
"1234".toInt()
//throw Exception("예외")
} catch (e: Exception) {
println("예외 발생")
} finally {
println("완료")
}
fun throwExceptionFun(): Nothing { //Nothing 타입 반환
throw IllegalArgumentException("throwingException")
}
val str: String? = null
val result = b ?: throwExceptionFun() // kotlin 은 해당 result 를 String 타입으로 인식한다.
Getter, Setter
class Cafe(
var name: String = "",
var id: Int = 0,
) {
// custom getter / setter 를 둘 수 있다. 로직이 길어질 경우 활용 가능
val brand: String
get() {
return "Starbucks"
}
var quantity: Int = 0 //기본 값을 지정
set(value) {
if(value > 0) { //수량이 0 이상인 경우에만 세팅하도록함
field = value //field 는 식별자로, 코틀린에서는 Get/setter 에 대해서 field 라는 식별자를 이용해 본 변수에 접근한다.
//quantity 라는 값을 의미하며, backingfield 라고 부른.
}
}
}
val cafe = Cafe()
cafe.id = 3 //프로퍼티를 이렇게도 사용 가능
println(${Cafe().brand})
Kotlin 의 클래스 상속
kotlin 의 모든 클래스들은 Any 를 상속받고 있다. 해당 클래스의 코드를 보면 Any 클래스와 그 함수는 Open 이라는 키워드를 사용하고 있다.
기본적으로 코틀린의 모든 클래스들은 final 로, 상속이 불가하다.
상속이 가능한 클래스를 생성해보자면 아래와 같다.
만일 open 된 클래스의 특정 메서드를 하위 클래스에서 재정의하지 않게끔 하고 싶다면 final 키워드를 활용할 수 있다.
open class Animal {
open var age: Int = 0
open fun move() {
println("---->")
}
final var live: Boolean = true
}
class Snake: Animal() {
override var age: Int = 0
//override var live = false // 불가
override fun move() { //override 키워드를 사용해야 상위 클래스에서 open 한 함수를 재정의할 수 있다.
println("~~~~~>")
}
}
class Bird(override var age: Int = 0): Animal() { // 기본 생성자를 이용해서도 오버라이드가 가능하다.
override fun move() {
println(" >")
}
}
class Dog: Animal() {
override var age: Int = 0
override fun move() {
super.move() // 부모 클래스의 구현부를 재사용
}
}
fun main() {
val birdy = Bird(age = 2)
println(birdy.age) //2
birdy.move() // >
}
추상 클래스
abstract 키워드를 사용하며, 구현하는 하위 클래스에서는 반드시 이를 구현해야만한다.
abstract class Developer {
abstract var age: Int
abstract fun code(language: String)
}
class BackendDeveloper(override var age: Int = 0): Developer() {
override fun code(language: String) {
println("I program with $language")
}
}
fun main() {
val backendDeveloper = BackendDeveloper(age = 20)
prinln(backendDeveloper.age) //20
backgendDeveloper.code("Kotlin") // I program with Kotlin
}
인터페이스
kotlin 에서 인터페이스를 정의하는 방법은 키워드 하나만 사용해서 가능하다.
java 와 동일하게 인터페이스도 상위 인터페이스를 지닐 수 있고, 복수개의 인터페이스를 구현할 수도 있다.
그러나, 동일한 시그니처를 가진 함수를 호출 시에 문제가 발생할 수 있는데 이때는 super<Cart>.add(product) 와 같이 꺽쇠를 이용해 특정 상위 인터페이스를 명시한다.
만일 동일한 시그니처를 가진 인터페이스 함수와 구현된 디폴트 함수가 동시에 존재한다면,
class Product(val name: String, val price: Int)
interface Cart {
var coin: Int
val weight: String
get() = "20kg" //field 즉 backing field 를 사용할 수 없다.
fun add(product: Product)
fun rent() {
if(coin > 0) {
println("카트를 대여합니다.")
}
}
}
class MyCart(override var coin: Int): Cart {
override fun add(product: Product) {
if(coin <= 0) println("코인을 넣어주세요")
else println("${product.name} 이 담겼습니다.")
}
}
fun main() {
val cart = MyCart(coin = 100)
cart.rent()
cart.add(Product("장난감", 1000)
}
enum class
enum class DevelopType(val id: Int): Available { //특정 interface 를 상속해 enum 클래스 안에 abstract 를 가지고 있는 것과 같은 효과를 볼 수 있다.
WEB(0) {
override fun isPayable(): Boolean = true
},
NATIVE(1){
override fun isPayable(): Boolean = true
},
WINDOW(2){
override fun isPayable(): Boolean = false
},
MAC(3){
override fun isPayable(): Boolean = false
}
//abstract fun isAvailable(): Boolean //각 값마다 특정 값 혹은 로직을 별도로 넣고 싶다면 abstract 로 구현할 수 있다.
}
interface Available {
fun isAvailable(): Boolean
}
fun main() {
if(DevelopType.WEB.isAvailable()) {
println("가능")
}
val type = DevelopType.valueOf("WINDOW") // 이렇게 가져올 수도 있고,
type == DevelopType.NATIVE // 비교도 가능하다.
for (status in PaymentStatus.values()) { // for 문에 하나하나 돌릴 수도 있다.
println("$status")
}
}
Collection
//immutable
//더이상 추가할 수 없다.
val immutableList = listOf(1,2,3)
//mutable
//add 등을 이용해 추가할 수 있다.
val mutableList = mutableListOf<Int>()
mutableList.add(1)
mutableList.add(2)
mutableList.add(3)
//apply 를 이용 가능
val mutableSet = mutableSetOf<Int>().apply {
add(1)
add(2)
add(3)
}
//map, set 또한 동일
//collection builder 는 내부에서는 mutable 이나, 반환은 immutable이다.
val numberList = buildList {
add(1)
add(2)
add(3)
}
for(number in numberList) {}
numberList.forEach {}
val iterator = numberList.iterator()
while(iterator.hasNext()) { iterator.next() }
//map 의 사용
val plusList = numberList.map { it+1 } // 공통 작업들을 직접 구현하지 않고 한줄로 줄일 수 있는 filter, map 을 자주 사용한다.
fval filteredList = numberList.filter { it%2 == 0 }
'안드로이드 > KOTLIN' 카테고리의 다른 글
[Kotlin] Kotlin <--> Java 의 예외처리 (+ 지정 키워드 escape 하여 사용하기) (0) | 2023.05.07 |
---|---|
[Kotlin] Kotlin <--> Java 클래스의 Getter, Setter 호출하기 (0) | 2023.05.07 |
[Kotlin] KtLint 로 클린한 코드 작성하기 (0) | 2023.01.30 |
[Kotlin] 함수/람다 함수 내의 반환 return (0) | 2023.01.21 |
[안드로이드] Coil 라이브러리로 이미지 로딩하기 (0) | 2023.01.08 |