익명 클래스로 리스너 구현하기
// 자바
button.setOnClickListener ( new OnClickListener() {
@Override
public void onClick(View view) { /* 클릭시 수행할 동작 */ }
});
// 코틀린
button.setOnClickListener { /* 클릭시 수행할 동작 */}
람다를 사용해서 최대값 찾기
val people = listOf(Person("앤디", 38), Person("실바", 32), Person("캐슬", 42))
println(people.maxBy { it.age })
// 멤버 참조를 사용하여 컬렉션 검색
println(people.minBy(Person::age))
// 람다식 문법
{ x: Int, y: Int -> x + y }
// 람다로 sum 함수 만들기
val sum = {x: Int, y: Int -> x + y}
println(sum(1,2))
run : 인자로 받은 람다를 실행해 주는 라이브러리 함수 이다.
run { println(42) }
people.maxBy
리팩토링 하기
// 연장자 찾기 정석 버전
people.maxBy({ p: Person -> p.age })
//가장 뒤에 있는 파라메터 람다는 밖으로 빼낼 수 있다.
people.maxBy() { p: Person -> p.age }
// 람다가 유일한 파라메터인 경우 괄호 삭제해도 됨
people.maxBy { p: Person -> p.age }
// 파라메터 타입 생략 하기 : 컴파일러가 타입을 자동 유출함. 못하는 경우도 있는데 그경우만 명시해주면 됨
people.maxBy { p -> p.age }
// 람다의 파라메터가 하나뿐이고 그 타입을 컴파일러가 추론할 수 있는 경우 it이라는 파라메터 명을 사용할 수 있음
people.maxBy { it.age }
// 람다를 변수에 저장할 때에는 파라메터 타입으 추론할 문맥이 존재하지 않음. 그러므로 파라메터 타입을 생략하면 안됨
getAge = { p: Person -> p.age }
people.maxBy(getAge)
함수의 파라메터를 람다에서 사용가능
fun printMessageWithPrefix(messages: Collection<String>, prefix:String) {
messages.forEach(
println("$prefix $it")
)
}
// output
Error : 403 forbidden
Error : 404 not found
fun printProblemCounts(responses: Collection<String>) {
var clientErrors = 0
var serverErrors = 0
responses.forEach {
if (it.startsWith("4")) {
clientErrors++
} else if (it.startsWith("5")) {
serverErrors++
}
}
println("$clientErrors client errors, $serverErrors server errors")
}
>>> val responses = listOf("200 OK", "418 I'm a teepot", "500 internal server error")
>>> printProblemCounts(responses)
// output
1 client errors, 1 server errors
fun tryToCountButtonClick(button: Button): Int {
var clicks = 0
button.Click { clicks++ }
return clicks
}
clicks++
부분이 함수가 다 실행되고 실행되기 때문이다. ::
연산자를 사용하자멤버 레퍼런스
라고 부른다.val getAge = Person::age
// 요거랑 동일함
val getAge = {person: Person -> person.age }
people.maxBy(Person::age)
// 탑레벨 멤버 레퍼런스 `::` 앞의 클래스를 생략
fun salute() = println("Salute!")
>>> run(::salute)
Salute!
val action = { person: Person, message: String -> sendEmail(person, message)}
val nextAction = ::sendEmail
생성자 레퍼런스
를 사용할 수 있다. data class Person(val name: String, val age: Int)
>>> val createPerson = ::Person
>>> val p = createPerson("Andy", 38)
>>> println(p)
Person(name=Andy, age=38)
fun Person.isAdult() = age >= 21
val predicate = Person::isAdult
isAdult는 Person의 멤버가 아니지만, 멤버처럼 사용할 수 있다.
data class Person(val name: String, val age: Int)
>>> val list = listOf(1, 2, 3, 4)
>>> println(list.filter { it % 2 == 0 })
[2, 4]
data class Point(val x: Int, val y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
>>> val p1 = Point(10, 20)
>>> val p2 = Point(30, 40)
>>> println(p1 + p2)
output
Point(x=40, y=60)
operator
키워드가 있어야 한다.+
기호로 두 객체를 더할 수 있다.a + b -> a.plus(b)
와 같이 작동한다. operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
오버로딩 가능한 산술 연산자
식 | 함수 이름 |
---|---|
a * b | times |
a / b | div |
a % b | rem |
a + b | plus |
a - b | minus |
a += b | plusAssign |
-a | unaryMinus |
+a | unaryPlus |
!a | not |
++a, a++ | inc |
--a, a-- | dec |
a == b | equals |
a < b, a > b, a <= b, a >= b | compareTo |
arr[1] | get |
arr[1] = 'a' | set |
.. | rangeTo |
for a in arr | iterator |
val (x, y) = p | component |
a == b
-> a?.equals(b) ? : (b == null)
a <= b
-> a.compageTo(b) <= b
로 컴파일됨start..end
-> start.rangeTo(end)
class Point(val x: Int, val y: Int) {
operator fun component1() = x
operator fun component2() = y
}
코틀린은 비트연산자를 지원하지 않음
자바 | 코틀린 |
---|---|
<< | shl |
>> | shr |
>>> | ushr |
& | and |
| | or |
^ | xor |
~ | inv |
문법
class Foo {
var p: Type by Delegate()
}
ex)
class Foo {
private val delegate = Delegate()
var p: Type
set(value: Type) = delegate.setValue(..., value)
get() = delegate.getValue(...)
}
지연 초기화를 위임 프로퍼티로 구현
class Person(val name: String) {
val emails by lazy { loadEmails(this) }
}
헬퍼클래스를 만들어서 프로퍼티 변경 통지 만들기
class ObservableProperty(
val propName: String, var propValue: Int,
val changeSupport: PropertyChangeSupport
) {
fun getValue(): Int = propValue
fun setValue(newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.firePropertyChange(propName, oldValue, newValue)
}
}
class Person(
val name: String, age: Int, salary: Int
) : PropertyChangeAware() {
val _age = ObservableProperty("age", age, changeSupport)
var age: Int
get() = _age.getValue()
set(value) { _age.setValue(value)}
val _salary = ObservableProperty("salary", salary, changeSupport)
var salary: Int
get() = _salary.getValue()
set(value) { _salary.setValue(value)}
}
ObservableProperty를 프로퍼티 위임으로 사용할 수 있도록 리팩토링
class ObservableProperty(
var propValue: Int, val changeSupport: PropertyChageSupport
) {
operator fun getValue(p: Person, prop: KProperty<*>): Int = propValue
operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
val oldValue = propValue
propValue = newValue
changeSupport.firePropertyChange(prop.name, oldValue, newValue)
}
}
class Person(
val name: String, age: Int, salary: Int
) : PropertyChangeAware() {
var age: Int by ObservableProperty(age, changeSupport)
var salary: Int by ObservableProperty(salary, changeSupport)
}
Delegates.observable 표준 라이브러리를 사용하기.
class Person(
val name: String, age: Int, salary: Int
) : PropertyChangeAware() {
private val observer = {
prop: KProperty<*>, oldValue: Int, newValue: Int ->
changeSupport.filrePropertyChange(prop.name, oldValue, newValue)
}
var age: Int by Delegates.observable(age, observer)
var salary: Int by Delegates.observable(salary, observer)
}
함수타입 선언하기
val sum = {x: Int, y: Int -> x + y}
val print = { println(42) }
(파라메터 타입, 파라메터 타입) -> 리턴 타입
이런식으로 좀 더 명시적으로 할 수 있다.
val sum: (Int, Int) -> Int = {x, y -> x + y}
val action: () -> Unit = {println(42)}
리턴타입이 null 이 되는 경우에는 아래와 같은 식으로 한다.
var canReturnNull: (Int, Int) -> Int? = { null }
noinline
키워드를 파라메터 앞에 붙여서 인라인으로 바꾸는 것을 금지할 수 있다. try-with-resource
와 같은 기능을 제공하는 use
라는 함수가 람다를 파라메터로 받으며, 인라인 함수이다.data class Person(val name:String, val age: Int)
val people = listOf(Person("Alice", 29), Person("Bob", 31))
fun lookForAlice(people: List<Person>) {
for (person in people) {
if (person.name == "Alice") {
println("Found!")
return
}
}
println("Not found")
}
>>> lookForAlice(people)
Found!
forEach
를 사용해도 된다. fun lookForAlice(people: List<Person>) {
people.forEach {
if (it.name == "Alice" ) {
println("Found!")
return
}
}
println("Not Found!")
}
nonlocal return
이라 부른다. local return
이라 부른다.fun lookforAlice(people: List<Person>) {
people.forEach label@ {
if (it.name == "Alice") return@label
}
println("Alice might be somewhere")
}
fun lookforAlice(people: List<Person>) {
people.forEach {
if (it.name == "Alice") return@forEach
}
println("Alice might be somewhere")
}
람다 말고 코드 볼록을 넘기는 방법으로 익명함수를 넘기는 방법이 있다.
fun lookforAlice(people: List<Person>) {
people.forEach (fun (person) {
if (person.name == "Alice") return // 가장가까운 함수를 가리킴
println("${people.name} is not Alice")
})
}
fun
함수를 리턴한다.