[Kotlin] 확장 함수(Extension Function)

곽호택·2021년 7월 21일
4

코틀린

목록 보기
1/6
post-thumbnail

[Kotlin] 확장 함수(Extension Function)

2021- 06 - 24

! 2021년도 1학기 IT창업 동아리 SOPT에 7차 세미나때 배웠던 내용을 정리하고 추가하기 위해서 작성하였고, 세미나 내용 + 코틀린 Document 도 추가로 활용하였다.

1. 코틀린의 확장 함수?

코틀린은 클래스에 상속하거나 디자인 패턴을 사용하지 않고 새로운 기능으로 클래스를 확장 할 수 있는 기능을 제공하는데 이것이 확장(extension)이라는 선언을 통해 이루어진다.

이때 추가적인 메소드를 구현하면 이를 "확장 함수"라고 하고 추가적인 프로퍼티를 구현하면 "확장 프로퍼티"라고 한다.

2. 확장 함수 언제 사용할까?

우리가 스스로 만든 클래스의 경우 새로운 함수가 필요할 때 언제든지 쉽게 추가할 수 있다. 이와 달리 외부 라이브러리(third-party library)를 사용할 때는 함수를 추가하기가 매우 어렵거나 변경할 수 없는 경우가 있다.

하지만 우리는 그 외부 라이브러리를 앱 개발시 사용하고자 한다. 이럴 경우, 코틀린에서 제공하는 확장 함수 기능을 통해 클래스를 확장하여 우리가 원하는 새로운 함수들을 입맛대로 만들 수가 있게 된다.

정리하자면, ''기존에 있던 클래스는 뼈대이고 주변에 새로운 함수나 프로퍼티를 붙여서 클래스의 크기를 키운다" 라고 생각하면 된다.

3. 확장 함수 만들어 보기!

확장 함수를 만들 때 알아야 하는 용어는 다음과 같이 두가지가 있다.

receiver type : 확장 대상이 될 클래스

receiver object : 확장 함수의 내부 구현 시 this 키워드를 사용하여 receiver type이 가지고 있는 public 인스턴스에 접근하는 객체

확장 함수를 만들기 위해서는 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 뿐만 아니라 다른 자료형에서도 인덱스 값의 변경이 가능해진다.

4. 확장 함수의 특징

  • 확장 함수는 상속이나 복잡한 디자인 패턴 없이 간단하게 확장 기능을 만들 수 있다.

  • 보일러플레이트 코드를 줄일 수가 있다.

  • 정적 바인딩 된다.

    정적 바인딩은 함수 호출 부분에 메모리 주소값을 저장하는 작업이 컴파일 시간에 행해지는 것으로 컴파일 이후의 값이 변경되지 않는 것을 의미한다.

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() 부분에 저장된 메모리 주소만을 알고 있어 이 위치에 있는 코드를 실행한다.

  • 함수나 프로퍼티의 이름이 겹치지 않도록 주의 하며, receiver type의 멤버 함수로 변수 타입, 매개변수 타입과 개수가 같은 함수가 있으면 무시된다.
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()과 이름이 같지만 ,매개 변수 타입이 달라서 출력이 이루어진다. 즉 오버로딩이 이루어지는 것이다.

profile
잘하고싶다

0개의 댓글