rex)
val set = hashSetOf(1,7,53)
val list = arrayListOf(1,7,53)
val map = hashMapOf(1 to "one", 7 to "seven")
여기서 만든 객체가 어떤 클래스에 속하는지알려면 ?
>>> println(set.javaClass)
class java.util.HashSet
>>> println(list.javaClass)
class java.util.ArrayList
>>> println(map.javaClass)
class java.util.HashMap
디폴트 구현과 달리 (1;2;3) 처럼 원소 사이를 세미콜론으로 구분하고 괄호로 리스트를 둘러싸고 싶다면?
// joinToString() 함수의 초기 구현
fun <T> joinToString (
collection: Colletion<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()
}
joinToString(collection, separator = " ", prefix = " ", postfix = ".")
코틀린으로 작성한 함수를 호출할 때는 함수에 전달하는 인자 중 일부의 이름을 명시할 수도 있다
호출 시 인자 중 어느 하나라도 이름을 명시하고 나면 혼동을 막기위해 그 뒤에 오는 모든 인자는 이름을 명시해야 한다
// 디폴트 파라미터 값을 사용해 joinToString() 정의
fun <T> joinToString (
collection: Colletion<T>,
separator: String = ", ",
prefix: String= "",
postfix: String = ""
): String {
...
}
>>> joinToString(list, ", ", "", "")
1, 2, 3
>>> joinToString(list)
1, 2, 3
>>> joinToString(list, ";")
1; 2; 3
이름 붙인 인자를 사용하는 경우에는 인자 목록의 중간에 있는 인자를 생략하고, 지정하고 싶은 인자를 이름을 붙여서 순서에 관계없이 지정 가능하다
함수의 디폴트 파라미터 값은 함수를 호출하는 쪽이 아닌
함수 선언 쪽에서 지정된다
파일에 대응하는 클래스의 이름 변경하기
코틀린 최상위 함수가 포함되는 클래스의 이름을 바꾸고 싶다면 파일에 @JvmName 애노테이션을 추가하라
-> 파일의 맨 앞, 패키지 이름 선언 이전에 위치해야 한다@file:JvmName("StringFunctions") <- 클래스 이름을 지정하는 애노테이션
package strings
fun joinToString( ... ): String { ... }호출 예시
ex) 자바
import strings.StringFunctions;
StringFunctions.joinToString(list, ", ", "", "");
-> 기존 자바 API를 재작성하지 않고도 코틀린이 제공하는 여러 편리한 기능을 사용할 수 있게 하는 역할
// 어떤 문자열의 마지막 문자를 돌려주는 메소드
package strings
fun String.lastChar(): Char = this.get(this.length - 1)
// String <- 수신 객체 타입
// this <- 수신 객체
>>> println("Kotlin".lastChar())
n
// Kotlin <- 수신 객체
확장 함수를 만들려면 추가하려는 함수 이름 앞에 그 함수가 확장할 클래스의 이름을 덧붙이기만 하면 된다
클래스 이름을 수신 객체 타입
확장 함수가 호출되는 대상이 되는 값(객체)을 수신 객체
this를 쓸 수도 생략할 수도 있다
fun String.lastChar(): Char = get(length - 1)
확장 함수 내부에서는 수신 객체의 메소드나 프로퍼티를 바로 사용할 수 있다
-> 확장 함수가 캡슐화를 깨지는 않는다
확장 함수안에서는 private/protected 멤버를 사용할 수 없다
1. 기본
import strings.lastChar
val a = "Kotlin".lastChar()
2. *를 사용한 임포트
import strings.*
val a = "Kotlin".lastChar()
3. as 키워드를 사용한 임포트
import strings.lastChar as last
val a = "Kotlin".last()
-> 이름을 바꿔서 임포트하면 이름 충돌을 막을 수 있다
// 확장 함수는 오버라이드할 수 없다
fun View.showOff() = pritnln("I'm a view!")
fun Button.showOff() = pritnln("I'm a button!")
>>> val view: View = Button()
>>> view.showOff() <- 확장 함수는 정적으로 결정된다
I'm a view!
↪ view가 가리키는 객체의 실제 타입이 Button이지만 이 경우 view의 타입이 View이기 때문에 무조건 View의 확장 함수가 호출된다
확장 함수를 첫 번째 인자가 수신 객체인 정적 자바 메소드로 컴파일한다는 사실을 기억하면 이런 동작을 쉽게 이해 가능
확장 프로퍼티를 사용하면 기존 클래스 객체에 대한 프로퍼티 형식의 구문을 사용할 수 있는 API를 추가할 수 있다
확장 프로퍼티는 아무 상태도 가질 수 없다
// 확장 프로퍼티 선언하기
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!
코틀린 언어 특성
- vararg 키워드를 호출하면 인자 개수가 달라질 수 있는 함수를 정의 가능
- 중위 함수 호출 구문을 사용하면 인자가 하나뿐인 메소드를 간편하게 호출 가능
- 구조 분해 선언을 사용하면 복합적인 값을 분해해서 여러 변수에 나눠 담을 수 있음
fun main(args:Array<String>) {
val list = listOf("args: *args)
// 스프레드 연산자가 배열의 내용을 펼쳐준다
}
val map = mapOf(1 to "one", 2 to "two")
// 두 호출은 동일하다
1.to("one") <- "to" 메소드를 일반적인 방식으로 호출
1 to "one" <- "to" 메소드를 중위 호출 방식으로 호출
인자가 하나뿐인 일반 메소드/확장 함수에 중위 호출을 사용할 수 있다
함수를 중위 호출하고 싶으면 infix 변경자를 함수 앞에 선언해야 한다
infix fun Any.to(other: Any) = Pair(this,other)
val (number, name) = 1 to "one"
코틀린에서는 자바의 split 대신에 여러 가지 다른 조합의 파라미터를 받는 split 확장 함수를 제공함으로써 혼동을 야기하는 메소드를 감춘다
정규식을 파라미터로 받는 함수는 String이 아닌
Regex 타입의 값을 받는다
// 마침표(.) , 대시(-)로 문자열을 분리하는 예시
println("12.345-6.A".split("\\.|-".toRegex()))
↪ 정규식을 명시적으로 만듦
. 을 \\ 로 이스케이프 하고 | 는 or의 의미를 나타낸다
그러므로 . 과 - 를 분리시킨다
[12, 345, 6, A]
↪ 정규식 안에서 마침표가 와일드카드 문자와 아닌 문자 자체로 쓰이게 하기 위해서 마침표를 이스케이스 시켰다
// 간단한 경우에는 꼭 정규식 쓸 필요 X
// 두 가지 모두 동일한 결과
>>>println("12.345.6.A".split(".","-"))
>>>println("12.345.6.A".split('.','-'))
ex)
val regex = """(.+)/(.+)\.(.+)""".toRegex()
첫 번째 그룹인 (.+)는 마지막 슬래시까지 모든 문자와 매치
마지막 슬래시를 제외한 모든 슬래시가 들어간다
두 번째 그룹은 마침표 전까지 모든 문자와 매치
세 번째 그룹은 나머지 모든 문자와 매치
3중 따옴표 문자열을 문자열 이스케이프를 피하기 위해서만 사용하지 않는다
줄 바꿈을 표현하는 아무 문자열이나 그대로 들어간다
여러 줄 문자열을 코드에서 더 보기 좋게 표현하고 싶으면
-> trimMargin을 사용해서 그 문자열과 그 직전의 공백을 제거
3중 따옴표 문자열 안에 문자열 템플릿을 사용할 수도 있다
-> 3중 따옴표 문자열 안에서는 이스케이프를 할 수 없기 때문에
$를 넣어야 한다면
val price = """${'$'}99.9""" 처럼 템플릿 안에 '$'를 써야함