[Kotlin] 3-2. 함수 정의와 호출

hansol_kim·2021년 9월 6일
0

kotlin 적응기

목록 보기
3/5
post-thumbnail

코틀린 공부 3일차

* 컬렉션, 문자열, 정규식을 다루기 위함 함수
* 이름 붙인 인자, 디폴트 파라미터 값, 중위 호출 문법
* 확장 함수와 확장 프로퍼티를 사용하여 자바 라이브러리 사용
* 최상위 및 로컬 함수와 프로퍼티를 사용해 코드 구조화

컬렉션 처리: 가변길이 인자, 중위함수 호출, 라이브러리 지원

자바 컬렉션 API 확장

3-1에서 코틀린은 자바의 컬렉션보다 더 많은 기능을 제공한다고 언급했다.

str.last()
numbers.max()

코틀린에서만 제공하는 lastmax함수는 모두 확장함수였던 것이다.

가변인자 함수: 인자의 개수가 달라질 수 있는 함수 정의

가변인자 함수를 알기위해서 우리는 코틀린에서의 listOf함수에 관심을 기울일 필요가 있다.

val list = listOf(2, 3, 5, 7, 11)

리스트를 생성하는 함수로 원하는 만큼 인자를 전달하여 생성할 수 있다. 여기서 중요한 것은 "원하는 만큼" 이다. 라이브러리에서 정의한 listOf함수를 보면 다음과 같다.

fun listOf<T>(vararg values: T) : List<T> { ... }

자바에서는 타입 뒤에 ...를 붙이는 대신 코틀린에서는 파라미터 앞에 vararg 변경자를 붙인다.

스프레드 연산자에 대한 내용은 추후 필요시 작성하도록 하겠다.

값의 쌍 다루기: 중위 호출과 구조 분해 선언

중위 호출이라는 특별한 방식으로 호출한 사례를 소개한다.

val map = mapOf(1 to "one", 2 to "two")

1.to("one") -> to 메소드를 일반 방식으로 호출
1 to "one"  -> to 메소드를 중위 호출 방식으로 호출

파라미터가 하나뿐인 일반, 확장함수에서 중위 호출을 사용할 수 있다. 중위 호출을 허용하고 싶으면 infix 변경자를 함수 선언 앞에 추가해야 한다. 이 부분도 자세한 내용은 추후 제대로 이해한 다음 작성하도록 하겠다.

코드 다듬기 : 로컬 함수와 확장

코드의 중복을 피하는 것은 개발자의 숙명이다. 코드의 중복을 피하기 위해 함수로 쪼개지만 클래스 내부에 작은 함수들이 너무 많아지고 오히려 코드를 이해하는데 어려움이 발생하기도 한다.

코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있는 기능인 로컬함수가 있다. 잘 와 닿지 않을 것 같아서 예시코드를 준비했다. 아래 코드를 보자.

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
	if (user.name.isEmpty()) { --- (1)
		throw IllegalArgumentException("Can't save user ${user.id} is empty Name")
	}
	
	if (user.address.isEmpty()) { --- (2)
		throw IllegalArgumentException("Can't save user ${user.id} is empty Address")
	}

	// save user
}

(1)과 (2)의 코드가 중복된다는 것을 볼 수 있고 이를 함수화 해야한다. 이를 로컬함수로 바꾸어 보자.

class User(val id: Int, val name: String, val address: String)

fun saveUser(user: User) {
	
	fun validate(user: User, value: String, fieldName: String) {
		if(value.isEmpty()) {
			throw IllegalArgumentException("Can't save user ${user.id} is "+
			"empty $fieldName")
		}
	}
	
	validate(user, user.name, "Name")
	validate(user, user.address, "Address")

	// save User
}

바꾸어보니 라인 수는 비슷할 지 몰라도 깔끔해졌을 뿐더러 추후 검증할 요소가 추가되더라도 쉽게 추가할 수 있다. (요소가 추가되면 될 수록 라인 수의 차이는 극명해질 것이다.)

그런데 validate를 호출할 때마다 user객체를 넘겨야하는 것은 불필요하다. → 로컬함수는 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있다.

fun saveUser(user: User) {
	
	... 생략 ...
	
	validate(user.name, "Name")
	validate(user.address, "Address")

	// save User
}

그리고 이 예제를 개선하고 싶다면 User의 확장함수를 validateBeforeSave라는 이름으로 호출하면 더욱 깔끔해질 수 있다.

하지만, 일반적으로 한 단계만 함수를 중첩시키라고 권장한다. 함수의 깊이가 깊어지면 코드를 읽는데 상당히 어려워진다.

profile
1주 1글 실천하자

0개의 댓글