코틀린에서 컬렉션 만들기
//HashSet
val set = hashSetOf(1, 7, 53)
//ArrayList
val list = arrayListOf(1, 7, 53)
//HashMap
val map = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
- setOf listOf mapOf 같은 함수들은
- 각각 Set List Map 타입의 인스턴스를 생성한다.
to
- 코틀린에서 제공하는 중위 함수이다.
- 키와 값을 연결하여 Pair 객체를 생성한다.
- 해당 Pair 객체는 Map 을 생성할 때 사용된다고 한다.
코틀린과 자바 컬렉션
- 자바 컬렉션과 동일한 클래스를 사용한다.
- 코틀린과 자바 간의 상호 운용성을 강화하기 위함
- 코틀린에서 자바 컬렉션에 확장 함수를 추가하여 코틀린만의 편리한 기능 제공
확장 함수의 예시
fun main() {
//리스트의 마지막 원소 가져오기
val strings = listOf("first", "second", "fourteenth")
val last = strings.last()
println("last = $last");
}
//last = fourteenth
fun main() {
val numbers = setOf(1, 14, 2)
println(numbers.maxOrNull());
}
//14
- max 함수의 경우 코틀린 1.4 이후에 maxOrNull 로 변경됨.
- 컬렉션이 비어있는 경우 null 을 반환한다.
함수를 호출하기 쉽게 만들기
기본 인자(Default Arguments)
- 함수에 기본 인자를 제공하는 경우 함수 호출시 해당 인자를 생략할 수 있다.
- 코드의 간소화!
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()
}
- 이런식으로 seperator, prefix, postfix 에 기본 값을 제공하였고,
- 해당 함수를 호출시 모든 인자를 명시하지 않아도 기본 인자로 제공된다.
명명된 인자(Named Arguments)
- 함수 호출 시 인자의 순서를 신경 쓰지 않고, 인자의 이름을 통해 직접 값을 전달가능하다.
fun main() {
println(joinToString(listOf(1, 2, 3), prefix = "[", postfix = "]", separator = " "));
}
//[1 2 3]
확장 함수 (Extension Functions)
- 기존 클래스에 새로운 메서드를 추가하는 것처럼 함수를 호출 가능하다.
- 컬렉션과 같은 표준 라이브러리 클래스에 유용함.
fun main() {
println(listOf(1, 2, 3).joinToString(separator = "; ", prefix = "(", postfix = ")"))
}
fun <T> Collection<T>.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()
}
//(1; 2; 3)
- Collection<T> 의 확장 함수로서, 모든 컬렉션 타입에 대해 직접 호출이 가능함 ㄷㄷ
디폴트 값과 자바
- 디폴트 파라미터 값은 코틀린의 유용한 기능임.
- 자바에는 해당 개념이 없기에, 자바 코드에서 코틀린 함수를 호출할 때에 모든 파라미터를 명시해야함.
- 자바와의 상호 운용성을 고려하여
@JvmOverloads
어노테이션 제공
- 해당 어노테이션을 코틀린 함수에 추가하는 경우,
- 코틀린 컴파일러가 자바에서 사용할 수 있도록 여러 오버로드된 메서드를 생성해줌.
- 각 오버로드된
메서드는 마지막 파라미터부터 하나씩 파라미터를 생략
하며, 생략된 파라미터에 대해서는 코틀린 함수에 정의된 디폴트 값을 사용한다.
@JvmOverloads
fun <T> joinToString(
collection: Collection<T>,
separator: String = ", ",
prefix: String = "",
postfix: String = ""
): String {
// 함수 구현
}
- 자바에서는
// 모든 파라미터를 명시한 호출
String result1 = joinToString(collection, ", ", "", "");
// postfix를 생략한 호출
String result2 = joinToString(collection, ", ", "");
// prefix와 postfix를 생략한 호출
String result3 = joinToString(collection, ", ");
// separator, prefix, postfix를 모두 생략한 호출
String result4 = joinToString(collection);
최상위 함수와 프로퍼티 사용 가이드
- 자바에서는 모든 코드를 클래스의 메서드로 작성해야한다.
- 코틀린에서는 불필요한 클래스 없이 최상위 수준에서 함수를 정의 가능하다.
- 코드 구조 단순화
- 유틸리티 클래스 남발 방지의 장점이 있음.
최상위 함수의 이점
- 간결성
- 유틸리티 클래스 없이 함수 직접 정의 가능.
- 접근성
- 다른 패키지에서 함수를 사용시, 유틸리티 클래스를 임포트할 필요없이 함수만 임포트하면 됨.
- 명확성
- 함수가 어떤 클래스에서 속하지 않기 때문에, 함수의 목적과 사용법이 더 명확함.
코틀린에서 최상위 함수 정의
- 파일명과 동일한 클래스의 정적 메서드로 컴파일된다.
// join.kt
package strings
fun joinToString(...): String { ... }
자바에서 코틀린 최상위 함수 호출하기
// 자바에서의 호출 예시
import strings.JoinKt;
String result = JoinKt.joinToString(list, "; ", "(", ")");
최상위 프로퍼티
// 파일의 최상위 수준에 상수 정의
const val MAX_COUNT = 100
- 자바에서
// 자바에서의 상수 접근
int maxCount = JoinKt.MAX_COUNT;
확장 함수와 확장 프로퍼티 사용 가이드
확장 함수의 개념
- 확장 함수
- 기존 클래스에 새로운 메서드를 추가하는 것처럼 사용할 수 있는 함수이다.
- 클래스 외부에 선언됨.
- 해당 클래스의 인스턴스에 대해 메서드처럼 호출됨
- 수신 객체 타입
- 확장 함수가 확장할 클래스의 타입이다.
- 수신 객체
- 확장 함수가 호출되는 대상이 되는 인스턴스 객체이다.
확장 함수의 정의
fun main() {
val joinToString = joinToString(listOf("Kotlin", "Java", "C++"), "; ")
joinToString.lastChar()
}
fun String.lastChar(): Char = this.get(this.length - 1)
//+
- 위 예제에서
String
은수신 객체 타입
이고, "Kotlin
"은수신 객체
입니다.
- 확장 함수는 클래스 내부의 메서드처럼
this를 사용하여 수신 객체에 접근
할 수 있습니다.
- 확장 함수는 클래스의
비공개(private)
또는보호된(protected)
멤버에 접근할 수 없습니다.
확장 프로퍼티
- 기존 클래스에 새로운 프로퍼티 추가 가능
- 백킹 필드를 가질 수 없으므로
- 초기화 불가능
- 게터
- 세터 명시적으로 정의해야함
백킹 필드
- 프로퍼티의 값을 저장하는 데 사용되는 숨겨진 필드
자바에서의 GETTER , SETTER 같음
- 프로퍼티에 접근하거나 프로퍼티 값을 설정시 백킹 필드를 사용해 값을 저장하고 검색
var name: String = "initial value" get() = field set(value) { field = value }
- field 는 현재 프로퍼티의 백킹 필드
- name 프로퍼티 값을 읽거나 쓸 때
- 실제로 field 를 통해 접근함.
- 코틀린 컴파일러가 자동으로 처리함.
- 프로퍼티의 값을 저장하는 데 사용되는 숨겨진 필드
정의
val String.lastChar: Char
get() = get(length - 1)
확장 프로퍼티 사용
println(joinToString.lastChar);
확장 함수와 오버라이드
- 코틀린에서 기존 클래스에 새로운 기능을 추가할 순 있지만..
- 오버라이딩은 불가능함.
- 확장함수가 클래스 일부가 아니라, 클래스 밖에서 선언되기 때문
확장 함수의 정적 디스패치
정적 디스패치(static dispatch)
확장 함수는 호출 시점에 수신 객체의 정적 타입에 따라 결정됩니다.
즉, 변수가 선언된 타입에 기반
하여 어떤 확장 함수가 호출될지 결정됩니다.
동적 디스패치(dynamic dispatch)
일반 멤버 함수는 실행 시점에 객체의 실제 타입에 따라 결정됩니다.
이는 오버라이드된 메서드가 있는 경우, 실제 객체 타입의 메서드가 호출
됩니다.
확장 함수 오버라이드 불가능
- 확장 함수는 클래스멤버가 아니라
- 상속 계층에서 오버라이딩이 불가능하다
- 확장 함수의 경우
- 컴파일 시점에 결정되고
수신 객체의 실제 타입이 아닌 선언된 타입에 따라 호출됨.
확장 함수의 정적 디스패치의 핵심
open class View {
open fun click() = println("View clicked")
}
class Button: View() {
override fun click() = println("Button clicked")
}
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")
val view: View = Button()
view.click() // "Button clicked" - 동적 디스패치
view.showOff() // "I'm a view!" - 정적 디스패치
- 수신 객체 선언된 타입
val view: View = Button()
- 확장 함수를 호출하는 경우 사용하는 변수의 타입이다.
val view : View = Button()
에서 view 의 선언 타입은View
임
- 수신 객체의 실제 타입
- 변수가 실제 참조하는 객체의 타입은
Button
이다.
- 변수가 실제 참조하는 객체의 타입은
⚒️ 확장 함수의 경우 컴파일러는 변수가 참조하는 객체의 실제 타입을 무시
- 변수의 선언된 타입인
View
에 따라 확장 함수를 호출한다.
예를 들어
fun View.showOff() = println("I'm a view!")
fun Button.showOff() = println("I'm a button!")
- 인 경우
View
타입의 변수에 Button 인스턴스를 할당함.
val view: View = Button()
- showOff 호출하는 경우
view.showOff() // "I'm a view!" 가 출력됨
확장 프로퍼티
- 기존 클래스 객체에 대한 프로퍼티 형식의 구문으로 사용할 수 있는 API를 추가
- 실제로는 상태를 가질 수 없지만, 프로퍼티 문법으로 더 짧게 코드를 작성가능함.
예시
- lastChar 함수를 프로퍼티로 변환
val String.lastChar: Char get() = get(length - 1)
- 일반 프로퍼티와 유사하지만, 수신 객체 클래스(String)이 추가되어 있고,
- 별도의 뒷받침하는 필드가 없기에 게터는 반드시 정의해야한다.
- 일반 프로퍼티와 유사하지만, 수신 객체 클래스(String)이 추가되어 있고,
변경 가능한 확장 프로퍼티
StringBuilder
의 경우 , 마지막 문자를 변경할 수 있기에var
키워드를 사용해 확장 프로퍼티를 만들 수 있다.var StringBuilder.lastChar: Char get() = get(length - 1) set(value: Char) { this.setCharAt(length - 1, value) }
- 사용
fun main() { val sb = StringBuilder("Kotlin?") sb.lastChar = '!' println(sb) }
Kotlin!
자바에서 확장 프로퍼티의 사용
- 확장 프로퍼티를 사용하기 위해 게터나 세터를 명시적으로 호출해야 한다.
StringUiltKt.getLastChar("Java")
같은 방식으로 호출한다.
- lastChar 함수를 프로퍼티로 변환
Uploaded by N2T
'코틀린 > 코틀린인액션' 카테고리의 다른 글
[KOTLIN] 문자열과 정규 (0) | 2024.02.09 |
---|---|
[KOTLIN] 함수 정의와 호출 (0) | 2024.02.08 |
[KOTLIN] try catch finally (0) | 2024.02.08 |
[코틀린인액션] 상속 제어 변경자 (0) | 2023.12.17 |
[코틀린인액션] 가시성 변경자 및 접근 제어 (0) | 2023.12.17 |