2021- 06 - 24
! 2021년도 1학기 IT창업 동아리 SOPT에 7차 세미나때 배웠던 내용을 정리하고 추가하기 위해서 작성하였고, 세미나 내용 + 코틀린 Document 도 추가로 활용하였다.
코틀린은 클래스에 상속하거나 디자인 패턴을 사용하지 않고 새로운 기능으로 클래스를 확장 할 수 있는 기능을 제공하는데 이것이 확장(extension)이라는 선언을 통해 이루어진다.
이때 추가적인 메소드를 구현하면 이를 "확장 함수"라고 하고 추가적인 프로퍼티를 구현하면 "확장 프로퍼티"라고 한다.
우리가 스스로 만든 클래스의 경우 새로운 함수가 필요할 때 언제든지 쉽게 추가할 수 있다. 이와 달리 외부 라이브러리(third-party library)를 사용할 때는 함수를 추가하기가 매우 어렵거나 변경할 수 없는 경우가 있다.
하지만 우리는 그 외부 라이브러리를 앱 개발시 사용하고자 한다. 이럴 경우, 코틀린에서 제공하는 확장 함수 기능을 통해 클래스를 확장하여 우리가 원하는 새로운 함수들을 입맛대로 만들 수가 있게 된다.
정리하자면, ''기존에 있던 클래스는 뼈대이고 주변에 새로운 함수나 프로퍼티를 붙여서 클래스의 크기를 키운다" 라고 생각하면 된다.
확장 함수를 만들 때 알아야 하는 용어는 다음과 같이 두가지가 있다.
확장 함수를 만들기 위해서는 receiver type에 .(온점)을 붙여 새로운 함수를 만들면 된다. 밑에 코드는 MutableList의 두 인덱스의 값을 변경해주는 swap() 확장 함수를 생성하는 것이다.
fun MutableList<Int>.swap(index1: Int, index2: Int){
val tmp = this[index1] // 'this'의 경우 MutableList에 해당한다.
this[index1] = this[index2]
this[index2] = tmp
}
swap함수 생성시에 MutableList라는 receiver type에 .(온점)을 붙여서 만들었다. 이를 호출할 때는 다음과 같이 호출하면 된다.
fun main(){
val list = mutableListOf(1, 2, 3)
list.swap(0,2) //swap()함수를 호출한다.
print(list)
}
//[3,2,1] 출력
확장 함수는 제너릭 타입에 관해서도 확장이 가능하다.
fun <T> MutableList<T>.swap(index1: Int, index2: Int){
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
이 경우에는 위에 우리가 사용했던 int 뿐만 아니라 다른 자료형에서도 인덱스 값의 변경이 가능해진다.
확장 함수는 상속이나 복잡한 디자인 패턴 없이 간단하게 확장 기능을 만들 수 있다.
보일러플레이트 코드를 줄일 수가 있다.
정적 바인딩 된다.
정적 바인딩은 함수 호출 부분에 메모리 주소값을 저장하는 작업이 컴파일 시간에 행해지는 것으로 컴파일 이후의 값이 변경되지 않는 것을 의미한다.
open class Shape
class Rectangle : Shape() //Rectangle 클래스가 Shape 클래스를 상속 받음.
fun Shape.getName() = "shape" //Shape 클래스의 확장 함수 getName()
fun Rectangle.getName() = "rectangle" //Rectangle 클래스의 확장함수 getName
//확장함수 호출
fun printClassName(s : Shape){
println(s.getName())
}
printClassName(Rectangle())
이 경우에는 "shape"가 출력 된다.
printClassName() 에 Rectangle 타입의 인스턴스를 전달했지만 확장 함수는 정적 바인딩 되므로 확장 함수 호출 부분에 저장되는 메모리 주소가 이미 컴파일 되는 시간에 결정되었다.
즉, 확장 함수 호출 부분인 s.getName() 부분에 이미 Shape 클래스의 확장 함수인 getName()함수가 저장된 메모리 주소가 저장되어 있다.
결국 printClassName(Rectangle())로 호출 하더라도 프로세서의 경우에는 s.getName() 부분에 저장된 메모리 주소만을 알고 있어 이 위치에 있는 코드를 실행한다.
class Car {
fun shape(str : String){
println("빨강")
}
fun num(int : Int){
println("2")
}
}
fun Car.shape(str : String){
println("노랑")
}
fun Car.num(str: String){
println("3")
}
fun main(){
Car().shape("A")
Car().num(1)
Car().num("B")
}
빨강
2
3
이런식으로 출력된다. Car.shape(str: String)의 경우 멤버 함수인 shape()와 이름도 같고, 매개 변수 타입도 같아서 출력되지 않는다.
하지만 Car.num(str: String)의 경우 멤버 함수인 num()과 이름이 같지만 ,매개 변수 타입이 달라서 출력이 이루어진다. 즉 오버로딩이 이루어지는 것이다.