3장에서는 함수 정의와 호출기능을 코틀린에서 어떻게 개선했는지에 대해 다뤘다.
하지만 자바보다 더 많은 기능 사용이 가능하다.
아래와 같은 함수가 존재한다.
fun <T> joinToString(
collection: Collection<T>,
separator: String,
prefix: String,
postfix: String
): String {
val result = StringBuilder(prefix)
for ((index, element) in collection.withIndex()) {
if (index > 0) result.append(separator) //첫 원소 앞에는 구분자를 붙이면 안됨
result.append(element)
}
result.append(postfix)
return result.toString()
}
아래와 같은 결과값을 갖는다.
val list = listOf(1, 2, 3)
println(joinToString(list, "; ", "(", ")"))
>> (1; 2; 3)
위 함수는 어떤 타입의 값을 원소로 하는 컬렉션이든 처리할 수 있으며 문법이 자바와 비슷하다. 잘 작동하지만 더 줄일 수 있는 방법이 있을 것 같다!
네가지의 파라미터를 다 기억하려면 너무 힘들다.
자바에서는 아래와 같은 형식으로 가독성을 해결하지만
joinToString(collection, /* separator */ " ", /* prefix */ * *, /* postfix */ ".");
코틀린에서는 이런식으로 해결한다.
joinToString(list, separator = " ", prefix = " ", postfix = ".")
그냥 보기에도 코틀린에서 지원하는 이름 붙인 인자를 사용한 코드가 더 깔끔하다!
@JvmOverloads
를 붙이면 하나의 코틀린 함수에서 오버로딩이 필요한 함수를 알아서 생성해줌아래 코드를 보자
fun <T> Collection<T>.joinToString(
separator: String = ", ", // ", "라는 기본값 지정
prefix: String = "", //""라는 기본값 지정
postfix: String = "" //""라는 기본값 지정
): String {//구현 코드}
사용 예시
joinToString(list, ", ", "", "") //기존과 동일
>> 1, 2, 3
joinToString(list) // separator, prefix, postfix 생략 -> 기본값 사용
>> 1, 2, 3
joinToString(list, "; ") // prefix, postfix 생략
>> 1; 2; 3
//아래와 같이 지정하고 싶은 인자를 이름을 붙여서 순서와 관계없이 지정 가능
joinToString(list, postfix=";", prefix="# ")
>> # 1, 2, 3;
//join.kt
package strings
fun joinToString(...): String {...}
//자바라면 JointKt라는 클래스를 생성해준다.
//클래스명을 바꾸기를 원한다면 @JvmName 사용
const val UNIX_LINE_SEPARATOR="\n"
public static final String UNIX_LINE_SEPARATOR="\n"
어떤 클래스의 멤버 메서드인 것처럼 호출가능하지만 해당 클래스 외부에서 선언된 함수 혹은 프로퍼티
package strings
//String : 수신객체타입 - 확장이 정의될 클래스의 타입
//this : 수신객체 - 그 클래스에 속한 인스턴스 객체
fun String.lastChar(): Char = this.get(this.length - 1)
import strings.lastChar
import strings.*
import strings.lastChar as last
val String.lastChar: Char //확장 프로퍼티
get() = get(length - 1)
var StringBuilder.lastChar: Char
get() = get(length - 1) //프로퍼티 게터
set(value: Char) { //프로퍼티 세터
this.setCharAt(length - 1, value)
}
println("Kotlin".lastChar) //n
val sb = StringBuilder("Kotlin?")
sb.lastChar = '!'
println(sb) //Kotlin!
코틀린의 컬렉션에는 여러가지 라이브러리를 제공한다.
val list = listOf(2,3,5,7,11)
listOf는 개발자가 원하는대로 값을 추가할 수 있다.
fun listOf<T>(vararg values:T) : List<T> {....}
//infix로 중위 호출에 사용하는 함수라는 것을 표시
infix fun Any.to(other:Any) = Pair(this, other)
val (number, name) = 1 to "one"
fun saveUser(user: User) {
if (user.name.isEmpty()) {
throw IllegalArgumentException(
"Can't save user ${user.id}: empty Name")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException(
"Can't save user ${user.id}: empty Address")
}
// Save user to the database
}
위 예제는 유효성체크하는 코드가 값마다 들어가있다.
2개만 있어서 많이 길지는 않지만 값이 더 늘어난다면 마음아픈 코드가 될 것이다.
fun saveUser(user: User) {
fun validate(user: User,
value: String,
fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException(
"Can't save user ${user.id}: empty $fieldName")
}
}
//검증 코드를 내부 함수로 만들어서 호출한다.
validate(user, user.name, "Name")
validate(user, user.address, "Address")
// Save user to the database
}
중복 코드가 많이 사라졌다.
하지만 validate함수의 user를 호출하는 부분이 신경쓰인다.
fun saveUser(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException(
"Can't save user ${user.id}: " +
"empty $fieldName")
//외부 함수의 파라미터에 직접 접근 가능
}
}
validate(user.name, "Name")
validate(user.address, "Address")
// Save user to the database
}
validate()함수에서 user를 넘기는 부분까지 사라져서 중복이 더 줄었다!
fun User.validateBeforeSave() {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException(
"Can't save user $id: empty $fieldName")
// User의 프로퍼티를 직접 사용 가능
//private과 protected value를 제외하고 확장 함수에서 수신객체 멤버변수 사용 가능
}
}
validate(name, "Name") //중복되는 user 파라미터 삭제
validate(address, "Address")
}
fun saveUser(user: User) {
user.validateBeforeSave() //확장 함수 호출
}
//saveUser(User(1, "", ""))