Recent Posts
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- table not found
- 개성국밥
- 코루틴 빌더
- tolerated
- mp4fpsmod
- 깡돼후
- kotlin
- 겨울 부산
- Value too long for column
- PersistenceContext
- 오블완
- preemption #
- 코루틴 컨텍스트
- pytest
- terminal
- taint
- JanusGateway
- JanusWebRTCServer
- Spring Batch
- VARCHAR (1)
- vfr video
- python
- 티스토리챌린지
- 자원부족
- 헥사고날아키텍처 #육각형아키텍처 #유스케이스
- PytestPluginManager
- JanusWebRTC
- JanusWebRTCGateway
- 달인막창
- k8s #kubernetes #쿠버네티스
Archives
너와 나의 스토리
[Kotlin] 함수 정의와 호출1 - 컬렉션/확장 함수/로컬 함수/문자열을 정규식으로 나누기 본문
Programming Language/Kotlin
[Kotlin] 함수 정의와 호출1 - 컬렉션/확장 함수/로컬 함수/문자열을 정규식으로 나누기
노는게제일좋아! 2021. 6. 12. 12:14반응형
1. 컬렉션 만들기
- 간단한 컬렉션들을 만들어보자
val set = hashSetOf(1, 7, 53)
val list = arrayListOf(1, 7, 53)
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
- 코틀린은 자체 컬렉션을 제공하지 않다. -> 기존 자바 컬렉션을 활용할 수 있음
- 코틀린 컬렉션은 자바 컬렉션과 똑같은 클래스지만 자바보다 더 많은 기능을 쓸 수 있다.
- 예: 리스트의 마지막 원소 가져오기. 수로 이뤄진 컬렉션에서 최댓값 찾기
val numbers = setOf(1, 14, 2)
println(numbers.max()) // output: 14
val set = hashSetOf(1, 7, 53)
println(set.max()) // output 53
val list = arrayListOf(1, 7, 53)
println(list.last()) // output: 53
2. 함수
- 예로 들 함수를 하나 구현해보려고 한다.
- 자바 컬렉션에는 디폴트 toString 구현이 들어있다. 이를 통해 컬렉션을 출력하면 다음과 같은 형태로 나온다
- [1, 7, 53]
val list = listOf(1, 7, 53)
println(list) // output: [1, 7, 53]
- 우리는 출력하는 형태를 지정하는 함수를 구현해 보려고 한다.
- 이 함수는 generic하다. 즉, 어떤 타입의 값을 원소로 하는 컬렉션이든 처리할 수 있다.
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()
}
- 이 함수를 사용해서 collection을 출력해보자
fun main(args: Array<String>) {
val list = listOf(1, 7, 53)
println(joinToString(list, "; ", "(", ")"))
// output: (1; 7; 53)
}
이름 붙인 인자
- joinToString() 메서드를 호출해보자.
- joinToString(collection, " ", " ", ".")
- 이런식으로 작성을 하면 인자로 전달한 각 문자열이 어떤 역할을 하는지 구분하기 힘들다.
- 코틀린에서는 다음과 같이 이름을 명시할 수 있다.
- joinToString(collection, separator = " ", prefix = " ", postfix = ".")
디폴트 파라미터 값
- 코틀린에서는 함수 선언에서 파라미터의 디폴트 값을 지정할 수 있다.
fun <T> joinToString(
collection: Collection<T>,
separator: String = ",",
prefix: String = "",
postfix: String = ""
) {
// ...
}
함수를 클래스 안에 작성할 필요 없다
- 자바에서는 모든 코드를 클래스의 메서드로 작성해야 하지만 코틀린에서는 그럴 필요 없다.
- 함수를 직접 소스 파일의 최상위 수준, 모든 다른 클래스의 밖에 위치시키면 된다.
- 그런 함수를 다른 패키지에서 임포트해서 사용하면 된다.
메서드를 다른 클래스에 추가: 확장 함수와 확장 프로퍼티
- 기존 자바 API를 재작성하지 않고도 코틀린이 제공하는 여러 편리한 기능을 사용할 수 있다.
- 이는 확장 함수(extension function)이 가능하기 때문이다.
- 확장 함수: 어떤 클래스의 멤버 메서드인 것처럼 호출할 수 있지만 그 클래스의 밖에 선언된 함수이다.
- 예: String 클래스에 문자열의 마지막 문자를 돌려주는 메서드를 추가
- this 생략 가능
package strings
fun String.lastChar(): Char = this.get(this.length - 1)
- 확장 함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이기만 하면 된다.
- 클래스 이름을 수신 객체 타입(receiver type)이라 부르며, 확장 함수가 호출되는 대상이 되는 값(객체)을 수신 객체(receiver object)라고 부른다.
- 위 예에서 String이 수신 객체 타입, this가 수신 객체가 된다.
- 위처럼 확장 함수를 정의해두면, 다음과 같이 일반 클래스 멤버를 호출하는 것처럼 해당 함수를 사용할 수 있다.
- But, 그 함수를 다른 클래스나 함수와 마찬가지로 임포트해야 한다.
println("Kotlin".lastChar())
- as 키워드를 사용하면 import한 클래스나 함수를 다른 이름으로 부를 수 있다.
import strings.lastChar as last
val c = "Kotlin".last()
- 아까 만든 joinToString() 함수를 확장 함수로 정의해보자
fun <T> Collection<T>.joinToString( // Collection 클래스에 joinToString 함수를 확장
separator: String = ",",
prefix: String = "",
postfix: String = ""
): String {
val result = StringBuilder(prefix)
for ((index, element) in this.withIndex()) {
if (index > 0) result.append(separator) // 첫 원소 앞에는 구분자를 붙이지 않음
result.append(element)
}
result.append(postfix)
return result.toString()
}
fun main(args: Array<String>) {
val list = listOf(1, 7, 53)
println(list.joinToString("; ", "(", ")"))
// output: (1; 7; 53)
}
- 이 함수를 '문자열의 컬렉션'에 대해서만 호출할 수 있도록 설정할 수도 있다.
fun Collection<String>.join(
separator: String = ",",
prefix: String = "",
postfix: String = ""
) = joinToString(separator, prefix, postfix)
fun main(args: Array<String>) {
val list = listOf(1, 7, 53)
// println(list.join("; ", "(", ")")) -> error
val list2 = listOf("abc", "def", "ghi")
println(list2.join("; ", "(", ")"))
// output: (abc; def; ghi)
}
3. 문자열을 정규식으로 나누기
- 자바에서 문자열 나누기(String.split())
- "12.345-6.A".split(".")를 시도하면 빈 배열이 반환된다.
- 이유는 split의 구분 문자열은 실제로는 정규식(regular expression)이기 때문이다.
- 정규식을 파라미터로 받는 함수는 String이 아닌 Regax 타입의 값을 받는다.
- 이때, 마침표(.)는 모든 문자를 나타내는 정규식으로 해석된다.
- 코틀린에서는 split 확장 함수를 제공함으로써 구분 문자열로 String 값을 쓸 수도 regax 타입의 값을 쓸수도 있다.
- 마침표(.)와 대시(-)로 문자열을 분리하는 예
- 정규식 문법은 자바와 동일
println("12.345-6.A".split("\\.|-".toRegex())) // 정규식을 명시적으로 만듦
// output: [12, 345, 6, A]
println("12.345-6.A".split(".","-")) // 구분 문자열을 일반 string으로 정의
// output: [12, 345, 6, A]
4. 로컬 함수
- 좋은 코드의 중요한 특징 중 하나는 중복이 없는 것이다.
- 많은 경우 메서드 추출(Extract Method) 리팩토링을 적용해서 긴 메서드를 부분 부분 나눠서 각 부분을 재활용할 수 있다.
- 하지만 그렇게 코드를 리팩토링하면 클래스 안에 작은 메서드가 많아지고 각 메서드 사이의 관계를 파악하기 힘들어서 코드를 이해하기 더 어려워질 수도 있다.
- 리팩토링을 진행해서 추출한 메서드를 별도의 내부 클래스 안에 넣으면 코드를 깔끔하게 조직할 수는 있지만, 그에 따른 불필요한 준비 코드가 늘어난다.
- 코틀린에서는 함수에서 추출한 함수를 원 함수 내부에 중첩시킬 수 있다.
- 이렇게 하면 문법적인 부가 비용을 들이지 않고도 깔끔하게 코드를 조직할 수 있다.
- 아래의 예제를 참고하여 코드 중복을 로컬 함수를 통해 제거해보자.
- 예:
- 다음 리스트에는 사용자를 데이터베이스에 저장하는 함수가 있다.
- 이때 데이터베이스에 사용자 객체를 저장하기 전에 각 필드를 검증해야 한다.
class User(val id: Int, val name: String, val address: String)
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"
)
}
}
fun main(args: Array<String>) {
saveUser(User(1, "",""))
// output: Can't save user 1: empty Name
}
- 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, "Adress")
}
- 이 코드를 더 개선하고 싶다면 검증 로직을 User 클래스를 확장한 함수로 만들 수도 있다.
class User(val id: Int, val name: String, val address: String)
fun User.validateBeforeSave(){
fun validate(value:String, fieldName:String){
if(value.isEmpty()){
throw IllegalArgumentException(
"Can't save user $id: empty $fieldName"
)
}
}
validate(name, "Name")
validate(address, "Adress")
}
fun saveUser(user: User) {
user.validateBeforeSave()
}
출처:
- [Kotlin IN ACTION]
반응형
'Programming Language > Kotlin' 카테고리의 다른 글
[Kotlin] 클래스와 인터페이스2 - 데이터 클래스/클래스 위임/object 키워드 사용 (0) | 2021.06.15 |
---|---|
[Kotlin] 클래스와 인터페이스1 - 초기화(주 생성자와 부 생성자)/가시성 변경자/프로퍼티 (0) | 2021.06.14 |
[Kotlin] 코틀린 기초2 - 스마트 캐스트, 이터레이션(for 문, 컬렉션이나 원소 검사), 예외 처리 (0) | 2021.06.11 |
[Kotlin] 코틀린 기초1 - 함수, 변수, 클래스, property, enum, when (1) | 2021.06.10 |
[Kotlin] Spring Boot로 RESTful 웹 서비스 만들기 (4) | 2021.06.09 |
Comments