개요
어차피 요즘 나오는 언어들이 추구하는 방향이 비슷한 것 같다. JS의 최신 문법들을 확인하다보면, 코틀린도 별반 다를 것 없다는 생각이 든다. 화살표 함수, 리터럴, 맵, 리듀스 등등 편의성을 향상시켜 놓은 것이 전부이다. 그렇기 때문에 빠르게 Cheating Sheet를 정리하겠다.
기본
1. Hello World 프로그램 구성
fun main(args: Array<String>){
pringln("Hello, World")
}
// fun : 함수 예약어 / main : 함수명 / args : 입력파라미터명 / Array<String> : 입력파라미터 타입
2. 함수 선언
fun sum(a:Int, b:Int): Int {
return a + b
}
// fun : 함수 예약어 / a,b : 입력파라미터명 / ':Int' : 입력, 출력 파라미터 타입
3. Single-Expression 함수
fun sum(a:Int, b:Int) = a + b
// '함수 선언부' = : one 라인 로직을 통해 return을 표현 가능
4. 변수 선언 with Nullable Types
var <propertyName>[: <PropertyType>] [= <property_initializer>][<getter>][<setter>] // [] 부분은 생략 가능(자동으로 추론됨), getter, setter 는 기본적으로 만들어지지만, 재정의할 수 있음.
val length: Int
// val : Read-only를 의미
// var : mutable을 의미
// val로 선언한 변수의 초기값을 변경할 수는 없지만, 일반적인 상수변수와 다름 → 단지 setter 가 없는, 바꿀 수 없는 것
// const라는 예약어를 이용해 상수 변수 선언, 최상위 레벨에서 선언할 때만 const 예약어 사용 가능
var name: String? = null
// ? : null을 의미 / name은 nullable(null값이 올 수 있음을 미리 정의)
// 기본적으로 null 대입이 허용되지 않지만 ? 을 이용하면 허용
// return text?.get(0) // text가 null이면 get 메서드는 호출되지 않으며, get의 리턴 값도 null이 된다.
// return editText?.getText()?.toString() // 둘 중 하나라도 null 이면 null 리턴
// 간혹 변수가 null 값이 아님이 확실한데도 Nullable로 선언된 경우(특히 Java로 정의된 메서드에 접근할 때) 변수의 이름 뒤에 !!를 붙여 Non-Null 타입으로 강제 캐스팅
// """ 으로 멀티라인 스트링 대입 가능
// as 연산자(캐스팅 연산자)
// as? 연산자는 ClassCastException이 발생해야 하는 상황에 에러 발생 없이 Null 리턴
// val x: String? = y as? String // 캐스팅에 실패할 경우 Exception이 발생하지 않고 x에 null값이 대입된다.
length = name?.length ?: 0
// name.length / name? : name은 ?(null)일 가능성이 있음 / '?: 기본값' : ?이 null을 의미, null일 경우 0
length = name?.length ?: return
// name.length / name이 null일 경우 return
length = name?.length ?: throw Error()
// name.length / name이 null일 경우 throw Error()
- 코틀린의 숫자 타입의 클래스들은 모두 Number 타입의 서브 클래스
- 문자(Characters)는 Number Type이 아니다.
- Number Type에 대한 자동 형 변형(implicit conversions for number)을 제공하지 않는다. : ToXXX() 변환 함수 사용
- 숫자 타입에 대입되는 데이터에 언더바(Underscore) 추가 가능(가독성 Up!)
ex) val oneMillion: Int = 1_000_000
- 코틀린 클래스의 최상위 클래스는 Any (Java의 Object 느낌)
- Unit : 기본 리턴 (java 의 void 느낌), 생략 가능
- Nothing : 의미 있는 데이터가 없다는 것을 명시적으로 선언하기 위해 사용하는 타입
제어 구조
1. If문 as an expression
fun bigger(a: Int, b: Int) = if (a > b) a else b
// return을 붙이지 않아도, 끝 statement를 return으로 취급한다.
2, For 반복문
val list = listOf("A", "B", "C")
for(element in list){
Pringle(element)
}
// in : for문 keyword
Iterator “ hasNext() 함수와 next() 함수를 이용해 순차적 이용
val list1= listOf<String>("hello","list")
val iterator1=list1.iterator()
while (iterator1.hasNext()){
println(iterator1.next())
}
3. When Expression
fun numberTypeName(x:Number) = when(x) {
0 -> "Zero". //동일여부 체크
in 1..4 -> "Four or less" //범위 체크
5,6,7 -> "Five To Seven" //멀티 벨류 체크
is Byte -> "Byte" //값 타입 체크
else -> "Other Value" //이외
}
// 편의성 있게, 유형 분류 가능 / Single Expression, 화살표 함수를 활용 / switch 상위호환
4. When Expression with Predicates
fun signAsSTring(x: Int) = when {
x < 0 -> "Negative"
x == 0 -> "Zero"
else -> "Positive
}
// 편의성 있게, 크기에 따른 분류 가능 / if 상위호환
클래스
1. 주 생성자
class Person(val name: String, var age: Int)
// name은 read-only 속성, age는 mutable 속성, 클래스 생성되고 나면, 이름은 변경불가
2. 상속
open class Person(val name: String){
open fun hello() = "Hello, I am $name" //오버로딩?
}
// JS의 템플릿 리터럴처럼 "" 안에 $변수로 표현
class PolishPErsion(name: String) : Person(name) {
override fun hello() = "Hi, I am $name"
}
3. 속성 with assessors(악세서리 형식)
class Person(var name: String ,var surname: String){
var fullName: String //변수지만, 함수와 같이 get, set의 로직을 정의
get() = "$name $surname" //비구조적 할당
set(value) {
val (first, rest) = value.split(" ", limit = 2)
name = first
surname = rest
}
}
// JS의 비구조적 할당처럼 val(first, rest) = 로 표현
4. Data 클래스
data class Person(val name: String, var age: Int)
val mike = Persion("Mike",23)
//data modifier를 추가해줄 경우
//toString, equals, hashCode, 비구조화 할당, copy 함수가 추가로 제공된다.
mike.toString() // 모든 주생성자 값을 출력
mike == Persion("Mike", 23) // 모든 주생성자를 비교해서, 완전히 같을 경우 True
mike.hashCode() == Person("Mike",23).hashCode() // 모든 주생성자를 참고해서 HashCode생성
val(name, age) = mike //주생성자를 비구조화하여, 개별 변수로 입력해줄 수 있음
val Jake = mike.copy(name = "Jake") //나머지는 다 동일한 상태에서, 클래스 객체 복사 가능
콜렉션 리터럴
1. List(링크드..? 리스트) 선언
listOf(1,2,3,4) // List<Int>
mutableListOf(1,2,3,4) // MutableList <Int>
List(4) {it * 2} // it는 iterator를 의미하며, 리스트의 개별 요소를 의미, it는 4에서 시작
// 선언에 이런식올 표현할 경우, 8,16,32,64... 식으로 무제한 증가
// 아니면 8,10,12,14일수도.. it값이 만약 4에서 시작해서 1씩증가한다면 가정한다면
// 콜렉션의 개별값이 이전값에서 *2씩 곱하는 값을 취한다는 의미이다.
2. Set(집합) 선언
setOf("A", "B" , "C") //Set<String>
mutableSetOf("A", "B", "C") //MutableSEt<String>
3. Array(배열) 선언
arrayOf('a', 'b'. 'c') //Array<Char> , 작은 따옴표는 Char형을 의미
4. Map(Key Value 맵) 선언
mapOf(1 to "A", 2 to "B") //Map<Int, String>
mutableMapOf(1 to "A", 2 to "B") //MutableMap<Int, String>
5. Sequence 선언
sequenceOf(4,3,2,1) // Sequence<Int>
generateSequence(4){it + 1} // Sequence<Int>
//it는 4에서 시작하여, 4,5,6,7,8 형태의 무한배열을 취한다.
//콜렉션의 개별값이 이전값에서 +1 씩 더하는 값을 취한다는 의미이다.
6 Pair 선언
1 to "A" //Pair<Int, String>
# 콜렉션 처리
students
.filter { it.passing && it.averageGrade > 4.0 }
// filter : 조건에 맞는 것만 남김 / it은 iterator의 줄임말로, 탐색 당시 해당 객체를 의미
.sortedByDescending {it.averageGrade}
// sortedByDescending : 각 객체의 averageGrade값 기준으로 정렬
.take(10)
//상위 10개만 취함
.sortedWith(compareBy({it.surname}, {it.name}))
//각 객체의 surname 기준으로 먼저 정렬한 후에, name 기준으로 정렬
generateSequence(0) { it + 1}
.filter {it % 2 == 0 }
//filter : 해당 조건을 만족하는 것만 남김
.map { it *3 }
//map : 모든 값에 일괄 매핑(적용)
.take(100)
//take : 상위 100개만 취함
.average()
//average. : 평균 계산
val list = listOf(1,2,3,4)
1. filter : 콜렉션 상에서, 조건에 해당하는 값만 남김
list.filter {it % 2 == 0 }
2. map : 콜렉션 상에 존재하는 모든 객체에 일괄 작업 적용
list.map {it * 2}
3. flatmap. : 콜렉션 상에 존재하는 모든 객체에 일괄 작업을 적용하되, N개의 객체가 존재하는 컬렉션을 N*m개의 객체가 존재하는 컬렉션으로 변경 가능
list.flatmap {listOf(it, it+10)} //[1,11,2,12,3,13,4,14]
4. fold/reduce : 콜렉션 상에 존재하는 모든 객체를 축적하여, 특정 값을 계산함
list.fold(0.0){ acc, i -> acc + I}
//acc값은 0.0부터 시작하되, i는 개별 iterator(it)를 의미함, acc값에 개별 it값들을 더해가는 방식으로 최종 acc 값을 반환
list.reduce{acc, I -> acc * I}
//acc값은 1부터 시작하되, i는 개별 iterator(it)를 의미함, acc값에 개별 it값들을 곱해가는 방식으로 최종 acc 값을 반환
5. forEach/onEach
list.forEach{ print(it) } //1234 출력 / 출력값 : Unit(기존 리스트에 변경값 적용)
list.onEach{ print(it) } //1234 출력 / 출력값 : [1,2,3,4] 리스트(변환된 새로운 리스트)
6. partition : 조건에 맞는 것이랑 맞지 않는 콜렉션을 두 파트로 나누어 줌
val (evenlist, oddlist) = list.partition { it % 2 == 0} //true, false에 따라
7. min/max/minBy/maxBy : 해당 콜렉션의 최솟값, 최댓값, 특정 조건을 일괄 적용했을 때, 최솟값
list.min() //1
list.minBy { -it } //4
//일괄 개별 객체에 -1 연산을 했을 때의 기준으로, 최소값
list.max() //4
list.maxBy{ -it } //1
8. first/firstBy : 해당 콜렉션의 가장 첫번쨰 값, 특정 조건을 만족하는 값중에서 첫번째값
list.first() //1
list.first { it% 2 == 0} //2
//해당 조건을 만족하는 값의 첫번째, 내부적으로 임시적 Filter 연산
9.count : 특정 조건에 맞는 값을 카운팅
list.count { it % 2 == 0} //2
10. sorted/sortedBy : 조건에 따라 정렬된 콜렉션 반환
list.sorted() //[1,2,3,4]
list.sortedBy {it % 2} //[2,4,1,3]
//개별 객체에 일괄 %2한 값을 기준으로 정렬(보통은 속성값이 들어감)
11. groupBy : 콜렉션상에 존재하는 모든 객체에 대해 일괄 연산 후, 계산된 연산결과값을 key값(특정 속성값이 될 수 있음)으로 하고, value는 해당하는 객체의 리스트 값으로 하여, 맵 콜렉션 형식으로 반환
list.groupBy {it % 2} // Map : {1=[1,3], 0=[3,4]}
12. distinct/distinctBy
list.distinct() //유일한 값을 가지는 요소만 남긴 콜렉션 반환
# Mutable vs Immutable 콜렉션 처리 함수 비교
val list = mutableListOf(3,4,2,1)
val sortedResult = list.sorted() //정렬된 새로운 콜렛션 반환
//기존 list 순서에는 영향을 안줌, 새로운 배열 반환
println(sortedResult) //[1,2,3,4]
println(list) //[3,4,2,1]
val sortResult = list.sort() //기존 Mutable List를 정렬
println(sortResult) //kotlin.Unit
println(list) //[1,2,3,4]
# 어떠한 Object(클래스)이든지, 확장(사용)가능한 Functions
this : apply / run,with
it : also / let
val dialog = Dialog().apply {
title = "Dialog Title"
onClick { print("Clicked")}
}
함수
1. 함수 타입
() -> Unit : 입력 파라미터도 없고, 출력 파라미터도 없을 경우, Unit 객체를 반환하는 함수
(Int, Int) -> Int : 두개의 정수 입력 파라미터와 한개의 정수 출력 파라미터로 구성된 함수
(()->Unit)-> Int : 함수를 입력 파라미터로 받고, 정수 출력 파라미터를 반환하는 함수
(Int) -> () -> Unit : 정수 입력 파라미터를 받고, 출력 파라미터로 함수 반환하는 함수[입력 파라미터는 전체를 항상 소괄호로 표현할 것]
# 함수 리터럴
val add:(Int, Int) -> Int = {I,k -> I + j}
// 간단한 Lambda Expression(함수형 표현식)
// (int, Int) -> Int : 특정유형의 함수라는 타입을 의미
// (I, k) : 함수의 입력 파라미터
// (I + j) : 함수 처리 및 출력값을 의미
// 화살표 함수로 실제 함수 구현부 표현
val printAndDouble: (Int) -> Int = {
println(it)
// 람다 표현식 내에서, 하나의 입력 파라미터만 받을 경우, it으로 받을 수 있다.
it * 2
// 람다 표현식 내에서, 마지막 표현식은 함수의 출력값이 된다.
}
val I = printAndDouble(10) //10
print(i) //20
# 확장 함수 : 특정 클래스 또는 기존 타입 클래스에 임의로 사용자 정의 함수를 정의하여, 확장 및 추가할 수 있다.
fun Int.isEven() = this % 2 == 0
// Int라는 기존 클래스에 isEven()이라는 함수 추가
print(2.isEven()) //true
fun List<Int>.average() = 1.0 * sum() /size
print(listOf(1,2,3,4).average()) //2.5
# Delegates(대표)
1. Lazy : 미리 계산하지 않고, 처음으로 해당 변수가 사용될 때, 계산한다(성능용도)
val I by lazy {print("init"); 10}
print(i) //init 10 출력
print(i) //10 출력
//
• lateinit은 var 타입만 가능하고 lazy는 val 타입만 가능
• lateinit은 primitive type은 불가능하나 lazy는 가능
• lateinit은 Non-null 타입만 가능하나 lazy는 둘 다 가능
• lateinit은 로컬 변수에서는 불가능 하나 lazy는 가능
2. notNull : 마지막으로 할당된 값을 반환, 어떠한 값도 설정된 적이 없었으면 에러반환(null값이면 안됨)
3. observable/vetoable : 값이 변할 경우에 콜백함수를 호출해줌, vetoable 함수는 새로운 값이 설정되었을 경우 호출됨
var name by observable("Unset"){pooled,new -> println("${p.name} 이 $old 에서 $new로 변경되었습니다.")}
name = "Marcin"
//name이 Unset에서 Marcin으로 변경되었습니다.
4. Map/MutableMap : 속성 이름에 의해 맵에 대한 값을 찾는다.
val map = mapOf("a" to 10)
val a by map //속성 이름만으로, 값 할당
print(a) // 10
접근 제어자
public(default) : 프로젝트 내 어디서나
private : 같은 클래스내에서만 Visible
protected : 같은 클래스와 상속받은 클래스 내에서만 Visible
Internal : 클래스가 접근가능하면 같은 모듈내에서만 Visible
# Variance Modifier
Number > Int
class Box<T> : Box<Number> 와 Box<Int>는 Invariance한 관계이다.
class Box<out T> : Box<Number> 와 Box<Int>는 covariance한 관계이다.
class Box<in T> : Box<Number> 와 Box<Int>는 contravariance한 관계이다.
출처
https://blog.kotlin-academy.com/kotlin-cheat-sheet-1137588c75a
https://one-delay.tistory.com/20?category=793470
최근댓글