- 문법
- 간단한 특징
- 변수, 상수
- 자료구조
- 조건문
- 반복문
- Try-Catch
- Method
- Class
- Interface
- Generic
- Elvis
- Extension
- This
- Expression
- Lambda
- Higher Order Function
- Single Abstract Method (SAM)
- 기타 키워드
- Standard Library
- 코딩 가이드
- Reference
- 변수 선언시
타입 생략
이 가능하다. - 세미콜론
;
생략이 가능하다 - 기본적으로 모든 변수, 상수는
non-null
타입이다. - 람다 함수의 기본 변수로
it
을 사용한다. - 메소드를
일급 객체
로 취급한다. getter
,setter
메소드를 호출할때클래스명.변수명 = value
으로 축약할 수 있다.- 객체의 함수나 프로퍼티를 임의로
확장 정의
할 수 있다. ==
는 값을 비교하며,===
는 객체 메모리 주소를 비교한다.- Kotlin에서
Abstract
와Interface
를 제외한 모든Class
는 기본적으로Final
이다.
String
: 문자열 타입.Int
: 정수 타입.Long
: 정수 타입.Int의 2배
Float
: 실수 타입.Double
: 실수 타입.Float의 2배
Boolean
: 논리타입.true/false
Any
: 모든 타입을 허용.Nothing
: 모든 타입을 허용하지 않음.(void)
Unit
: return 타입 없음.
val string: String = "string"
val int: Int = 100
val long: Long = 100L
val float: Float = 10.0f
val double: Double = 10.0
val boolean: Boolean = true
var
: 변수val
: 상수const
:object
나companion object
내에서const val
과 같이 사용한다. Java에서클래스명.변수명
으로 접근이 가능하다.
val intValue = 100
val strValue: String = "string"
var intVariable = 10
var strVariable: String = "string"
object ObjectClass {
const var SINGLETON_STRING: Int = 100 // error.
const val SINGLETON_STRING: String = "STRING" // success.
}
intValue = 10 // error
intVariable = 100 // success
strValue = "hello" // error.
strVariable = "hello" // success.
as
: Kotlin에서 타입 캐스팅을 하기위한 예약어다.as?
:TypeCastException
에 안전하게 캐스팅한다.?:
: Elvis를 사용하면 실패시default value
를 설정할 수 있다.
"string" as Int // Exception
"string" as? Int // return null
"string" as? Int ?: "exception" // return "exception
Kotlin
은 기본적으로 모든 변수, 상수에 대해서non-null
타입이다.?
: 변수 타입 뒤에 사용하며,null
을 허용하거나nullable
객체에 안전하게 접근할 수 있다.!!
: 객체에 접근할 때 사용하며,non-null
임을 명시한다.?:
: Elvis를 사용하면 실패시default value
를 설정할 수 있다.
var strVariable1: String = null // error.
var strVariable2: String? = null // succes.
strVariable2.toString() // warning.
strVariable2?.toString() // .toString()을 실행하지 않는다.
strVariable2?.toString() ?: "exception" // .toString()을 실행하지 않고 "exception"을 리턴한다.
strVariable = "string"
strVariable2.toString() // 변수를 할당해도 선언 타입이 String? 이므로 warning이 나타난다.
strVariable2!!.toString() // success.
-
변수에 접근시 초기화 하는
늦은 초기화
를 제공한다. -
lateinit
var
와 함께 사용한다.primitive-type
에는 사용할 수 없다.getter
,setter
를 사용할 수 없다.::{name}.isInitialized
를 사용하여 초기화 체크를 할 수 있다.
-
by lazy
val
과 함께 사용한다.getter
를 사용할 수 없다.
-
var
,val
에서 각각 사용한다는 차이점 외에 동작 방식에는 차이가 없다.
lateinit var a: String
a = "lateinit"
if(::a.isInitialized) // true
val b: String by lazy {
"string"
}
- 따로 메소드를 정의하지 않아도 변수에 대한
getter
,setter
가 자동으로 생성된다. get()
:getter
메소드이며,var
,val
관계 없이 오버라이드 할 수 있다.클래스명.변수명
으로 접근할 수 있다.
set()
:setter
메소드이며,var
변수에서만 오버라이드 할 수 있다.클래스명.변수명 = setValue
로 값을 변경할 수 있다.
get()
혹은set()
메소드 내부에서field
키워드를 통해 값에 접근할 수 있다.
Class Example {
val value: Int = 10
get() = field * 100
var variable: Int = 10
get() = field * 100
set(value) {
field = value * 100
}
}
val example: Example = Example()
example.variable = 10 // 10을 저장했지만 set()을 통해 100배 한 값이 저장된다.
println(example.value) // 실제 값은 10이지만 get()을 통해 100배한 값이 리턴된다.
- 순서
(Index)
가 있는 자료구조. .get(index)
혹은[index]
로 접근할 수 있다..set(index, value)
혹은[index] = value
로 변경할 수 있다.- 정적 배열
(Array)
과, 동적 배열(List)
이 있다. - 동적 배열은 읽기 전용
(read only)
과 읽기, 쓰기(mutable)
모두 가능한 객체로 나누어 제공된다.
- 배열의
size
가 고정적이다. - 따라서 아이템의 추가, 삭제가 불가능하다.
- 해당 인덱스의 아이템에 접근해서 값을 가져오거나 변경할 수는 있다.
- 기본적인 생성 방법은
arrayOf<T>( ... )
이다. Primitive Type
배열 생성자를 제공한다.intArrayOf( ... )
charArrayOf( ... )
BooleanArrayOf( ... )
- 기타 등등
// 정적 배열 생성
val staticArray: Array<Int> = arrayOf(0, 1, 2)
// 아이템 접근
staticArray[0]
staticArray.get(0)
// 아이템 변경
staticArray[1] = 10
staticArray.set(1, 10)
// 배열의 사이즈 가져오기
staticArray.size
- 아이템에 접근만 가능한 배열이다.
- 배열 초기화 이후 아이템의 추가, 삭제가 불가능하다.
// 읽기 전용 배열 생성
val readOnlyList: List<Int> = listOf(0, 1, 2);
// 아이템 접근
readOnlyList[0]
readOnlyList.get(0)
// 배열의 사이즈 가져오기
readOnlyList.size
- 배열의
size
가 동적이다. Mutable Collection
은+
,-
연산자로 추가, 삭제가 가능하다.List
의 경우 우항에item
이 들어간다.
- 따라서 아이템의
CRUD
가 가능하다.
// 읽기, 쓰기 전용 배열 생성
val mutableList: MutableList<Int> = mutableListOf(0, 1, 2);
// 아이템 접근
mutableList[0]
mutableList.get(0)
// 아이템 변경
mutableList[1] = 20
mutableList.set(1, 20)
// 아이템 추가
mutableList += 100
mutableList.add(100)
// 아이템 삭제
mutableList -= 100
mutableList.remove(100)
// 배열의 사이즈 가져오기
mutableList.size
key
와value
로 구성된 자료구조다.- 순서가 정해져 있지 않다.
.get(key)
또는[key]
로 접근할 수 있다..keys
로Set<KEY>
를 가져올 수 있다..values
로Set<VALUE>
를 가져올 수 있다..size
로 Map의size
를 가져올 수 있다.- 특정
key
에 접근시 등록된 값이 없을때default value
로 Elvis나 제공되는method
를 사용하면 된다.getOrDefault(key, defaultValue)
: 기본값에 해당하는 매개변수 타입이<VALUE>
다.getOrElse(key, defaltValue)
: 기본값에 해당하는 매개변수 타입이<VALUE>
를 리턴하는 함수다
List
와 같이Read Only
와Mutable
객체가 각각 제공된다.
Item
의 추가나 삭제 없이 접근만 가능한Map
이다.
// 읽기 전용 맵 생성
val readOnlyMap: Map<String, Int> = mapOf(
"first" to 1,
"second" to 2,
"third" to 3
);
// 키 값에 해당하는 객체에 접근
readOnlyMap["first"]
readOnlyMap.get("two")
// 키 값에 해당하는 객체가 없을 경우 사용할 기본값을 설정
readOnlyMap["four"] ?: 10
readOnlyMap.getOrElse("five", {
...
10
})
readOnlyMap.getOrDefault("six", 10)
Item
의 추가나 삭제가 가능한Map
이다.Mutable
한Collection
은+
,-
로 아이템을 변경할 수 있다.Map
의 경우 우항에Key
값이 들어간다.
// 읽기, 쓰기 전용 맵 생성
val mutableMap: MutableMap<String, Int> = mutableMapOf(
"first" to 1,
"second" to 2,
"third" to 3
);
// 키 값에 해당하는 객체에 접근
mutableMap["first"]
mutableMap.get("two")
// 키 값에 해당하는 아이템 추가 및 변경
mutableMap += "key" to 100
mutableMap["key"] = 100
mutableMap.put("key", 100)
// 키 값에 해당하는 아이템 삭제
mutableMap -= key
mutableMap.remove("key")
// 키 값에 해당하는 객체가 없을 경우 사용할 기본값을 설정
mutableMap["four"] ?: 10
mutableMap.getOrElse("five", {
...
10
})
mutableMap.getOrDefault("six", 10)
List
와 비슷하지만 아래와 같은 차이점이 있다.- 순서가 없다.
- 중복을 허용하지 않는다.
List
와 같이Read Only
와Mutable
객체가 각각 제공된다.- 사용법은 List와 비슷하므로 생략.
==
: 값 자체를 비교한다.===
: 메모리 주소를 비교한다.is
: 객체 타입을 비교한다.in
: 해당 값이 범위에 포함되었는지 비교한다.
var i = 10
var s1 = "string"
var s2 = "string"
println(i == 10) // true
println(s1 == "string") // true
println(s1 == s2) // true
println(s1 === s2) // false
println(i in 0..9) // false
println(s1 is String) // true
Java
와 사용법이 같다.Kotlin
에는 3항 연산자가 없기 때문에if-else
를 한줄로 쓰는 방법을 사용한다.- 블록의 마지막 라인이
return
으로 작용한다.
if ( ... ) {
...
} else if ( ... ) {
...
} else {
...
}
var variable: Boolean = if ( ... ) {
true
} else {
false
}
val value: String = if (true) "true" else "false"
Java
의switch-case
와 비슷하다.if-else
와 같이 블록의 마지막 라인은return
이다.- 비교 조건이 유연하다.
when( ... ) {
1 -> {
...
}
1, 10 -> {
...
}
"string" -> {
...
}
is String -> {
...
}
!in 1..10 -> {
...
}
else -> {
...
}
}
- array
(String, array 등)
- 아이템을 인덱스 순서대로 하나씩 꺼내면서 순회한다.
변수명.indices
로 인덱스에 접근할 수 있다.변수명.withIndex()
로 인덱스와 값에 동시에 접근할 수 있다.
- map
- map의 아이템을 하나씩 꺼내면서 순회한다.
item.key
:로 키에 접근할 수 있다.item.value
:로 값에 접근할 수 있다.
변수명.keys
를 통해 키를 하나씩 꺼내면서 순회한다.변수명.values
를 통해 값을 하나씩 꺼내면서 순회한다.
- map의 아이템을 하나씩 꺼내면서 순회한다.
for (i in 0..10) {
...
}
for (i in 0..10 step 2) {
...
}
Item
을 하나씩 꺼내는 방법.
val array: Array<Int> = arrayOf(1, 2, 3);
for (item in array) {
...
}
Index
를 하나씩 꺼내는 방법.
val array: Array<Int> = arrayOf(1, 2, 3);
for (index in array.indices) {
...
}
Index
와Item
을 동시에 하나씩 꺼내는 방법.
val array: Array<Int> = arrayOf(1, 2, 3);
for ((index, item) in array.withIndex()) {
...
}
item
을 하나씩 꺼내는 방법.
val map: MutableMap<Int, String> = mutableMapOf(
1 to "first",
2 to "second",
3 to "third"
)
for (item in map) {
item.key
item.value
...
}
for ((key, value) in map) {
...
}
key
를 하나씩 꺼내는 방법.
val map: MutableMap<Int, String> = mutableMapOf(
1 to "first",
2 to "second",
3 to "third"
)
for (key in map.keys) {
...
}
value
를 하나씩 꺼내는 방법.
val map: MutableMap<Int, String> = mutableMapOf(
1 to "first",
2 to "second",
3 to "third"
)
for (value in map.values) {
...
}
Java
와 같다.
while( ... ) {
...
}
- 블록의 마지막 라인을
return
한다. - 그 외에는
Java
와 같다.
try {
...
} catch(e: Exception) {
...
} finally {
...
}
- 매개변수에
default value
를 설정할 수 있으며, 함수 호출시 해당 인자를 생략할 수 있다.. - 함수 호출 시
매개변수명=값
을 통해 명시할 수 있다. - 리턴 타입이
Unit
인 경우 생략한다. - 메소드 블록이 한줄인 경우
{ }
생략하고=
를 사용한다. 일급 객체
이므로 변수에 할당할 수 있다.override
키워드를 통해 확장할 수 있다.
// 기본형
fun getInt(): Int {
return 10
}
// 축약형
fun getString(): String = "return String"
// 기본값 설정
fun plus(a: Int = 10, b: Int = 20) = a + b
// 매개변수 순서에 관계 없이 이름으로 직접 지정하기
plus(b = 10) // return = 20
- 클래스와 메소드, 변수 기본적으로
final
이다. open
키워드를 통해final
속성을 제거할 수 있다.init { ... }
구문을 통해 초기화 작업을 할 수 있다.constructor
는 생략할 수 있다.constructor
를 통해 받는 매개변수는 전역변수로써 사용할 수 있다.- 생성자
overload
시 리턴 타입으로this()
로 최상위 생성자를 호출해야 한다. - 다만 생성자 오버로드 없이 최상위 생성자에
default value
를 설정하는 방법이 좋다. - 클래스 상속을 위해
:
을 사용한다. open
변수를override
할 수 있다.- 추가로 Implements 할 Interface는 상속 클래스 옆
,
으로 연속해서 정의한다.
open class KotlinClass constructor(val intValue: Int = 10): SuperClass(), Interface {
init {
...
}
override fun overrideMethod() {
...
}
}
- 클래스 내부에 클래스 정의를 통해 정의한다.
- 이때
inner class
를 통해 정의하면 일반적인inner class
가 생성된다. inner
없이class
만 사용하여 정의한 경우static inner class
가 생성된다.
class OuterClass {
init {
...
}
class StaticInnerClass {
...
}
inner class InnerClass {
...
}
}
Singleton
을 쉽게 생성해주는 클래스다.- 블록 내에 선언된 객체는
Singleton
으로 생성된다. 클래스명.변수명
으로 접근한다.
object ObjectClass {
const var SINGLETON_STRING: Int = 100 // error.
const val SINGLETON_STRING: String = "STRING" // success.
}
static
변수 또는 메소드를 정의한다.클래스명.변수명
으로 접근한다.object
와 차이접은Singleton
과static
의 차이다.
class CompanionObject {
init {
...
}
companion object {
const val intValue: Int = 100
fun getValue(): Int = intValue
}
}
POJO
클래스다..equals()
,.hashCode()
,.toString()
함수가 미리 정의되어있다.
data class DataClass(
val intVal: Int,
val strVal: String
...
)
- 기본적인 사용법은
Java
와 같다. abstract
는open
을 포함하고 있다.- 따라서
open
을 명시하지 않아도 상속 및 변수, 함수 오버라이드가 가능하다.
- 따라서
abstract class AbstractClass {
abstract val abstractValue: String
abstract fun abstractFunction(): String
}
class ChildClass : AbstractClass() {
override val abstractValue: String = "abstract value"
override fun abstractFunction() = this.abstractValue
}
- 기본적인 사용법은
Java
와 같다. open
을 명시하지 않아도 상속 및 변수와 메소드를 오버라이드 할 수 있다.- 익명 클래스로써 사용할 경우
object
키워드와 같이 사용한다. - 단
callback
의 경우 Higher-Order-Function을 사용하는것이 좋다.
class Button {
private var onClickListener: OnClickListener? = null
fun onClick() {
this.onClickListener?.onClick()
}
fun setOnClickListener(listener: OnClickListener) {
this.onClickListener = listener
}
interface OnClickListener {
fun onClick()
}
}
val button: Button = Button()
button.setOnClickListener(object: Button.OnClickListener {
override fun onClick() {
...
}
})
button.onClick()
- 기본적인 사용법은
Java
와 같다. <>
사이에 사용할 타입을 입력한다.<T>
:getter/setter
제한이 없는 제너릭이다.<in T>
: Input(setter)
전용 제너릭이다.<? super T>
로 사용할 수 있다.<out T>
: Output(getter)
전용 제너릭이다.<? extends T>
로 사용할 수 있다.<*>
: 모든 타입을 혀용한다는 점에서<Any>
와 비슷하나 전자 타입을 추론한다는 의미이며, 후자는<Object>
라는 의미다.- Java와 달리 제너릭을 생략할 수 없으며, 반드시 명시해야 한다. 단 타입 추론이 가능한 경우에는 생략 가능.
interface Generic<out T> {
fun getItem(): T // success.
fun setItem(item: T) // error.
}
class Sample : Generic<String> {
init {
...
}
override fun getValue(): String {
...
}
fun <E> genericFunction(): E {
...
}
fun genericFunction(list: List<*>) {
...
}
}
val sample: Sample = Sample()
val getItem1: Any = sample.getValue()
if (getItem1 is String) // true
val getItem2: Int = sample.genericFunction<Int>() // Generic 생략 가능
val getItem3: String = sample.genericFunction<String>() // Generic 생략 가능
genericFunction(mutableList<Int>())
genericFunction(mutableList<String>())
Kotlin
에서는?
연산자를 통해Nullable
한 객체에 안전하게 접근할 수 있다. 접근에 실패했을 경우에instance?.length ?: value
로defalt value
를 설정할 수 있다.- 같은 원리로 타입 캐스팅에 실패했을 경우에 대한 값을 지정할 수 있다.
var strVariable:String? = null;
var length1: Int = strVariable?.length // return null
var length2: Int = strVariable?.length ?: 0 // return 0
var test2: String = length2 as? String ?: "0"
- 특정 클래스를 임의로
확장
할 수 있다. this
: 값에 접근할 수 있다.infix
: 확장 메소드의 매개변수가 하나인 경우에만 사용할 수 있다.object.method(value)
에서 아래와 같이 축약할 수 있다.object method value
property
명이 겹치는 경우 기존에 정의한 값이 우선순위가 높다.
fun Int.increase(): Int = this + 1
infix fun Int.sum(value: Int): Int = this + value
var intValue1 = 10.increase() // return 11
var intValue2 = 10 sum 5 // return 15
this
: 호출된 시점 혹은 위치에 따라 this가 가르키는 객체가 달라질 수 있다.메소드 내부
: 메소드를 가지고 있는 클래스의 인스턴스.익명 클래스(인터페이스) 내부
: 익명 클래스의 인스턴스.이너 클래스
: 이너 클래스의 인스턴스.람다식 내부
: 람다식이 호출된 클래스의 인스턴스.- higher order function: 람다식이 호출된 클래스의 인스턴스.
class ExampleA {
val mSampleB: SampleB by lazy {
SampleB()
}
inner class ExampleB {
fun test(a: String) {
println("d.hashcode = ${this.hashCode()}")
a.foo()
}
fun Int.foo() {
println("e.hashcode = ${this.hashCode()}")
}
}
fun test() {
println("a.hashcode = ${this.hashCode()}")
val mutableList: MutableList<Int> = mutableListOf(1)
mutableList.forEach {
println("b.hashcode = ${this.hashCode()}")
}
object: Interface {
override fun onStart() {
println("c.hashcode = ${this.hashCode()}")
}
}.onStart()
mSampleB.test("string")
}
interface Interface {
fun onStart()
}
}
val ExampleA: exampleA = ExampleA()
exampleA.test()
/**
a.hashcode = 91062265 // @ExampleA
b.hashcode = 91062265 // @ExampleA
c.hashcode = 75874921 // Anonymous Class
d.hashcode = 255746728 // @ExampleB
e.hashcode = -891985903 // String Extension
**/
{name}@
같이 명시할 수 있다.- 예) 내부 for문에서 외부 for문을
break@outer-for
- 예) 내부 for문에서 외부 for문을
- 클래스명, 함수명 등 직접 정의하지 않아도
@{class-name}
으로 사용할 수 있다.- 예) 내부 클래스에서
this@OuterClass
로 외부 클래스로 접근.
- 예) 내부 클래스에서
loopI@ for (i in 1..10) {
loopJ@ for (j in 1..100) {
if (j == 10) {
println()
break@loopI
// loopI에 해당하는 for문이 종료된다.
}
print("$j ")
}
}
class ExampleA {
val mSampleB: SampleB by lazy {
SampleB()
}
inner class ExampleB {
fun test() {
val a = this@ExampleA
val b = this@ExampleB
println("a.hashcode = ${a.hashCode()}")
println("b.hashcode = ${b.hashCode()}")
}
}
fun test() {
mSampleB.test()
}
}
val ExampleA: exampleA = ExampleA()
exampleA.test()
{ param1, param2 -> ... }
: Kotlin의 람다식이다.it
: 람다식의 파라미터가한개
인 경우 매개변수 정의를생략
하고 사용할 수 있다.- 블록의 마지막 줄은
return
이다.
val list: MutableList<Int> =
mutableListOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.filter {
it % 2 == 0;
}.map {
it * 10;
}.forEachIndexed { index, value ->
println("index = $index // value = $value");
}
- Kotlin의 메소드는
1급 객체
이다. - 따라서 함수를 인자로
전달
하거나리턴
할 수 있다. - 변수에도
저장
할 수 있다. - 매개변수로 함수가 하나 필요한 경우 소괄호
()
를생략
할 수 있다. Lambda
로축약
해서 사용할 수 있다.
// 기본형
val toString: (Int) -> String = fun(int: Int): String {
return int.toString()
}
// 축약형
val toInt: (String) -> Int = {
it.toInt()
}
fun method(function: () -> Unit) {
function()
}
method {
...
}
fun method(functionA: () -> Unit, functionB: () -> Unit) {
functionA()
functionB()
}
method ({
// functionA ...
}, {
// functionB ...
})
Java
에서하나
의 메소드를 가지는Interface
혹은abstract class
와 그setter
를 정의해야 함.- 예를들면
Android
의View.OnClickListener
와view.setOnClickListener(l);
- 예를들면
- 원래
view.setOnClickListener(object: View.OnClickListener { .. })
와 같이 선언해야 하는것을축약
할 수 있다.view.setOnClickListener { ... }
val button: Button by lazy {
findViewById(R.id.button)
}
button.setOnClickListener {
...
}
- 매개변수로
Higher-Order-Function
을 필요로 하는 함수에 사용한다. - 고차함수를 사용할 때 성능상에 발생하는 패널티를 해소한다.
- 바이트 코드를 조작하여
liline
키워드를 포함한 함수의 내용이 호출한 곳에 직접적으로 삽입된다. - 결과적으로 코드량이 증가하는 단점이 있지만 합리적으로 잘 활용한다면 성능에서 많은 이점이 있다.
- 고차함수 매개변수가 2개 이상이고, 그중 어떤것은
liline
을 사용하고 싶지 않을 때 변수 앞에noinline
을 사용한다. - 매개변수로 전달받은 람다를 호출할 때 함수 몸체에서 직접 호출하지 않는 경우(내부 함수, 람다 )에는
crossinline
을 사용한다.
inline fun inlineFunction(blockA: () -> Unit)
inline fun inlineFunction(blockA: () -> Unit, noinline blockB: () -> Unit)
inline fun inlineFunction(blockA: () -> Unit, crossinline blockB: () -> Unit) {
Runnable {
blockB() // noinline이나 crossinline이 없는 경우 error 발생
}
}
// Java였으면 value instance T는 syntax error가 발생한다.
inline fun <reified T> inlineFunction(value: Any): Boolean = value is T
- 주로 객체의
null
체크를 할때 사용한다. - 블록 내에서
it
를 사용하여 객체에 접근할 수 있다. - 블록의 마지막 줄이 리턴된다.
run
과 큰 차이가 없다.let
의 경우 블록이 접근한 객체를 매개변수로 같는lambda
식이기 때문에it
으로 접근한다.run
의 경우 블록이 접근한 객체를 확장한extension
한 것으로this
로 접근한다.
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
val name: String? = null;
name?.let {
...
} ?: let {
...
}
- 객체의 생성과 동시에 값을 초기화할 때 유용하다.
Builder
패턴같은 느낌으로 사용.- 블록 내에서
this
를 사용하여 객체에 접근할 수 있다. - 접근한 객제 자신이 다시 리턴된다.
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
class User(
var age: Int = 0,
var name: String = "",
var email: String = ""
);
val user: User = User().apply {
this.age = 21;
this.name = "seokchan.kwon";
this.email = "seokchan.kwon@gmail.com"
}
- 이미 생성한 객체에 대하여 재 접근시 주로 사용한다.
- 블록 내에서
this
를 사용하여 객체에 접근할 수 있다. - 블록의 마지막 줄이 리턴된다.
let
과 큰 차이가 없다.let
의 경우 블록이 접근한 객체를 매개변수로 같는lambda
식이기 때문에it
으로 접근한다.run
의 경우 블록이 접근한 객체를 확장한extension
한 것으로this
로 접근한다.
public inline fun <T, R> T.run(block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block()
}
class User(
var age: Int = 0
var name: String = ""
var email: String = ""
);
val user: User = User()
user.run {
this.age = 23;
}
- 이미 생성한 객체에 대하여 재 접근시 주로 사용한다.
- 블록 내에서
this
를 사용하여 객체에 접근할 수 있다. - 블록의 마지막 줄이 리턴된다.
run
과 용도가 거의 비슷해 보인다.
public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return receiver.block()
}
class Button(
var text: String = "",
var textSize: Float = 12f,
var textColor: String = "#3dbff0"
);
val button: Button = Button()
with(button) {
this.textSize = 21f;
this.textColor = "#ffffff";
}
- 블록 내에서
it
를 사용하여 객체에 접근할 수 있다. - 접근한 객체 자신이 리턴된다.
apply
와 비슷하다.also
: 블록이lambda
식이기 때문에it
으로 객체에 접근한다.apply
: 블록이extension function
이기 때문에this
로 접근한다.
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
fun User.copy() = User().also {
it.age = this.age;
it.name = this.name;
it.email = this.email;
}
val userCopy: User = user.copy();
- 블록 내에서
it
를 사용하여 객체에 접근할 수 있다. - 블록의 마지막 줄에서
Boolean
을 리턴한다. - 리턴된 값이
true
인 경우접근한 객체
가 리턴되고,false
인 경우null
이 리턴된다.
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (predicate(this)) this else null
}
class User(
var age: Int = 0
var name: String = ""
var email: String = ""
);
val user: User? = User().takeIf {
it.age > 0
}
if (user == null) // true
- 블록 에서
it
를 사용하여 객체에 접근할 수 있다.ㅎ - 블록의 마지막 줄에서
Boolean
을 리턴한다. - 리턴된 값이
false
인 경우접근한 객체
가 리턴되고,true
인 경우null
이 리턴된다. takeIf
와 반대다.
public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
contract {
callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
}
return if (!predicate(this)) this else null
}
class User(
var age: Int = 0
var name: String = ""
var email: String = ""
);
val user: User? = User().takeIf {
it.age > 0
}
if (user == null) // false