val strings: List<String> = listOf("first", "second", "last")
println(strings.last())
>>> last
실제 구현은 다음과 같이 List의 확장함수로 구현되어 있다.
val numbers: Collection<Int> =setOf(1, 14, 2)
println(numbers.max())
→ 코틀린 1.4 버전 이후로 deprecated되었고, maxOfNull()을 사용한다.
위에서 사용한 listOf()는 다음과 같이 정의되어 있다.
public fun <T> listOf(vararg elements: T): List<T> = if (elements.size > 0) elements.asList() else emptyList()
...
vararg
이미 배열인 원소를 가변 길이 인자로 넘기기
fun main(args: Array<String>) {
val list = listOf("args: ", *args)
println(list)
}
val map = mapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
와 같이 map을 만들때, to는 중위호출로 to라는 일반 메소드를 호출한다.
infix fun Any.to(other: Any) = Pair(this, other)
위와 같이 선언된 to()는 Pair의 구조 분해 선언으로 두 변수를 즉시 초기화 한다.
val (number, name) = 1 to "one"
for ((index, element) in collection.withIndex()) {
println("$index: $element")
}
파라미터로 Regex 타입을 받는다.
println("12.345-6.A".split("\\.|-".toRegex()))
>>> [12, 345, 6, A]
→ 코틀린 정규식 문법은 자바와 같음
자바 split 오버로딩
println("12.345-6.A".split(".", "-"))
>>> [12, 345, 6, A]
→ 여러 separator 사용 가능
파일 path를 디렉토리, 파일명, 확장자로 구분하기
fun parsePath(path: String) {
val directory = path.substringBeforeLast("/")
val fullName = path.substringAfterLast("/")
val fileName = fullName.substringBeforeLast(".")
val extension = fullName.substringAfterLast("/")
println("Dir: $directory, name: $fileName, ext: $extension")
}
fun main() {
parsePath("/Users/yole/kotlin-book/chapter.doc")
}
fun parsePath(path: String) {
val regex = """(.+)/(.+)\.(.+)""".toRegex()
val matchResult = regex.matchEntire(path)
if (matchResult != null) {
val (directory, filename, extension) = matchResult.destructured
println("Dir: $directory, name: $filename, ext: $extension")
}
}
fun main() {
parsePath("/Users/yole/kotlin-book/chapter.doc")
}
→ """"" : 삼중 따옴표 문자열에서는 어떤 문자도 이스케이프 할 필요가 x
fun main() {
val kotlinLogo = """| //
.| //
.|/\"""
println(kotlinLogo.trimMargin("."))
}
>>> | //
>>> | //
>>> |/\
→ 와 같이 문자열이 텍스트가 포함해도 쉽게 문자열로 변환할 수 있음
import java.lang.IllegalArgumentException
class User(val id: Int, val name: String, val address: String)
fun saveUser(user: User) {
if (user.name.isEmpty()) {
throw IllegalArgumentException("name null")
}
if (user.address.isEmpty()) {
throw IllegalArgumentException("address null")
}
}
fun main() {
saveUser(User(1, "", ""))
}
→ 함수가 복잡하지는 않지만, 검증하는 코드가 반복된다
import java.lang.IllegalArgumentException
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("$[user.id]: null $fieldName")
}
}
validate(user, user.name, "Name")
validate(user, user.address, "Address")
}
fun main() {
saveUser(User(1, "", ""))
}
→ 중복되는 검증 부분을 메소드로 추출하고, 원래 함수 내부에 중첩
→ user 객체를 로컬 함수에 하나하나 전달해야 된다
import java.lang.IllegalArgumentException
class User(val id: Int, val name: String, val address: String)
fun saveUser(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("$[user.id]: null $fieldName")
}
}
validate(user.name, "Name")
validate(user.address, "Address")
}
fun main() {
saveUser(User(1, "", ""))
}
→ 로컬 함수가 자신이 속한 바깥 함수의 모든 파라미터와 변수를 사용할 수 있음을 이용
import java.lang.IllegalArgumentException
class User(val id: Int, val name: String, val address: String)
fun User.validateBeforeSave(user: User) {
fun validate(value: String, fieldName: String) {
if (value.isEmpty()) {
throw IllegalArgumentException("$[user.id]: null $fieldName")
}
}
validate(name, "Name")
validate(address, "Address")
}
fun saveUser(user: User) {
user.validateBeforeSave(user)
}
fun main() {
saveUser(User(1, "", ""))
}
→ 확장 함수로 추출
: 이 검증 로직이 User 외에 다른 데에서는 쓰이지 않아서 포함시키고 싶지 않은 경우