어떤 언어 기능과 미리 정해진 이름의 함수를 연결해주는 기법 = 관례(convention)
코틀린이 지원하는 convention 사용법에 대해 알아보자!
산술 연산자 오버로딩
- 자바 : + 연산자를 이용할 수 있는 타입은 정해져 있다.
- 코틀린 : 어디서든 일반 산술 연산자를 선언할 수 있다.
data class Point(var x: Int, var y: Int) {
operator fun plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}
}
>> p1+p2 // 와 같은 연산이 가능
a + b 는 컴파일 시점에는 자바처럼 a.plus(b)로 변한다.
operator fun Point.plus(other:Point):Point {
return Point(x+other.x, y+other.y)
}
식 | 함수 이름 |
* | times |
+ | plus |
/ | div |
- | minus |
% | mod |
두 피연산자의 타입이 다른 연산자 정의하기
operator fun Point.times(scale: Double):Point {
return Point((x*scale).toInt(),(y*scale).toInt())
}
- 코틀린에서는 교환 법칙 지원하지 않음 p*1.5는 되지만, 1.5*p 는 안됨
복합 대입 연산자 오버로딩 ( compound assignment )
>>> var point = Point(1,2)
>>> point += Point(2,3)
단항 연산자 오버로딩
식 | 함수 이름 |
+a | unaryPlus |
-a | unaryMinus |
!a | not |
++a, a++ | inc |
--a, a-- | dec |
비교 연산자 오버로딩
- 동등자 연산자 : equals()
a == b -> a?.equals(b) ?: ( b==null) 로 컴파일이 됨
data class Point(var x: Int, var y: Int) {
override fun equals(obj: Any?): Boolean {
if (obj === this) return true // 최적화
if (obj !is Point) return false
return obj.x == x && obj.y == y
}
}
- operator를 안 붙여도 되는 이유 ? 상위 클래스에 정의된 메소드를 오버라이딩 하기 때문
컬렉션과 범위에 대해 쓸 수 있는 관례
get, set convention
operator fun Point.get(index:Int) :Int {
return when(index) {
0 -> x
1 -> y
else ->
throw IndexOutOfBoundsException("Invalid coordinate $index")
}
}
>>> var point = Point(10,20)
>>> println(point[0])
20
data class MutuablePoint(var x:Int, var y:Int)
operator fun MutuablePoint.set(index:Int, value:Int) {
when(index) {
0 -> x=value
1 -> y=value
else ->
throw IndexOutOfBoundsException("Invalid coordinate $index")
}
}
>>> var point = Point(10,20)
>>> point[0]=20
>>> println(point)
MutuablePoint(x=20,y=20)
in convention
data class Rectangle(val upperLeft:Point, val lowerRight:Point)
operator fun Rectangle.contains(p:Point) : Boolean {
return p.x in upperLeft.x until lowerRight.x &&
p.y in upperLeft.y until lowerRight.y
}
rangeTo convention
>>> val now = LocalDate.now()
>>> val vacation = now .. now.plusDays(10) // = now.rangeTo(now.plusDays(10))
>>> println(now.plusWeeks(1) in vacation )
true
- rangeTo 함수는 Comparable에 대한 확장 함수
for 루프를 위한 iterator 관례
operator fun ClosedRange<LocalDate>.iterator() : Iterator<LocalDate> =
object : Iterator<LocalDate> {
var current = start
override fun hasNext() = current <= endInclusive
override fun next() = current.apply {
current = plustDays(1)
}
}
}
구조 분해 선언과 component 함수
>>> val p = Point(10,20)
>>> val (x,y) = p
>>> println(x)
10
>>> println(y)
20
val ( x,y ) = p 는 아래와 같이 컴파일 된다
val a = p.component1()
val b = p.component2()
// 구조 분해 선언을 사용해 여러 값 반환하기
data class NameComponents(val name:String, val extension:String)
fun splitFilename( fullName : String): NameComponents {
val result = fullName.split('.',limit=2)
return NameComponents(result[0],result[1])
}
val (name,ext) = splitFilename("example.kt")
구조 분해 선언과 루프
fun printEntries(map : Map<String, String>) {
for( (key,value) in map) {
println("$key -> $value")
}
}
프로퍼티 접근자 로직 재활용 : 위임 프로퍼티
코틀린이 제공해주는 관례중 독특하면서도 가장 중요한 기능이 위임 프로퍼티(delegated property)라고 한다.
위임 프로퍼티를 사용하면 어떻게 코드를 작성할 수 있는지 살펴보자
1. 직접 구현하기
open class PropertyChangeAware {
protected val changeSupport = PropertyChangeSupport(this)
fun addPropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.addPropertyChangeListener(listener)
}
fun removePropertyChangeListener(listener: PropertyChangeListener) {
changeSupport.removePropertyChangeListener(listener)
}
}
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
var age: Int = age
set(newValue) {
val oldValue = field // backing field
field = newValue
changeSupport.firePropertyChange("age", oldValue, newValue)
}
var salary: Int = salary
set(newValue) {
val oldValue = field // backing field
field = newValue
changeSupport.firePropertyChange("salary", oldValue, newValue)
}
}
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)
}
}
'Kotlin' 카테고리의 다른 글
Kotlin in Action - 1장. 코틀린이란? 왜 필요한가? (0) | 2020.04.04 |
---|---|
Kotlin in Action 11장 : DSL 만들기 (0) | 2020.04.03 |
Kotlin In Action - 5장. 람다로 프로그래밍 (0) | 2020.03.29 |
Kotlin In Action - 3장.함수 정의와 호출 (0) | 2020.03.29 |
Kotlin In Action - 2장. 코틀린 기초 (0) | 2020.03.29 |