val setExample = hashSetOf(1, 7, 53)
val listExample = arrayListOf(1, 7, 53)
val mapExample = hashMapOf(1 to "one", 7 to "seven", 53 to "fifty-three")
println(setExample.javaClass)
println(listExample.javaClass)
println(mapExample.javaClass)
결과
: 코틀린은 자신만의 컬렉션을 제공하지 않고, 기존 자바 컬렉션을 활용한다.
→ 왜? 자바 코드와의 상호작용을 위해서 굳이 컬렉션을 서로 변환할 필요가 없게 한다.
val strings = listOf("first", "second", "fourteenth")
println(strings.last())
:코틀린은 기존 자바 컬렉션을 활용하지만, 자바보다 더 많은 기능을 쓸 수 있다.
import java.util.HashSet;
class Main {
public static void main(String args[]) {
HashSet set = new HashSet();
set.add("abc");
set.add("efg");
set.add("hij");
System.out.println(set.toString());
}
}
>>> [abc, efg, hij]
→ 원하는 형식으로 toString()을 재정의 하려면?
: 구아바, 아파치 커먼즈 같은 라이브러리 혹은 직접 관련 로직 구현해야 한다 → 번거롭다
방법 1) 기본 호출
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, 4)
>>> println(joinToString(list, ":", "(", ")"))
>>> (1:2:4)
: 의도대로 잘 작동한다. 하지만? 호출 부분이 번거롭다
→ 인자로 전달한 separator, prefix, postfix가 각각 어떤 역할하는지 구분이 어렵고, 매번 함수의 시그니처를 찾아봐야함
방법2) 이름 붙힌 인자
>>> println(joinToString(list, separator = ":", prefix = "(", postfix = ")"))
: 전달하는 인자 중 일부/전부의 이름을 명시할 수 있다.
→ 인자 중 하나라도 이름을 명시하면 그 뒤에 오는 모든 인자는 이름을 꼭 명시해야 한다??
명시하지 않아도 잘 컴파일이 된다 ..?
방법 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()
}
>>> println(joinToString(list)
>>> println(joinToString(list, ":") // 일부를 생략하면 맨 뒤에 n개가 생략된다.
@JvmOverloads
: 코틀린 컴파일러가 자동으로 맨 마지막 파라미터로 부터 하나씩 생략한, 오버로딩된 자바 메소드를 추가함String joinToString(Collection<T> collection, String seperator, String prefix, String postfix);
String joinToString(Collection<T> collection, String seperator, String prefix);
String joinToString(Collection<T> collection, String seperator);
String joinToString(Collection<T> collection);
와 같은 네개의 오버로딩한 함수가 만들어진다. 생략된 파라미터는 코틀린 디폴트 파라미터 값을 사용정적 유틸리티 클래스?
: 다양한 정적 메소드를 모아두는 역할로 특별한 상태나 인스턴스 메소드가 없는 클래스
코틀린에서의 정적 유틸리티 클래스
: 코틀린 파일의 모든 최상위 함수는 해당 클래스의 정적 메소드가 된다.
package strings
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()
}
→ 이 함수의 실행 과정
: 컴파일러가 join.kt를 컴파일할때 새로운 클래스 정의 → JVM에서 호출할때는
pakage strings;
public class JoinKt {
public static String joinToString(...) {...}
}
와 같이 컴파일된다.
@JvmNames
어노테이션을 사용한다.var opCount = 0
fun performOperation() {
opCount++
}
fun reportOperationCount() {
println("Operation performed $opCount times")
}
fun main() {
performOperation()
performOperation()
performOperation()
reportOperationCount()
}
>>> Operation performed 3 times
: 최상위 프로퍼티를 활용해서 코드에 상수를 추가할 수 있음
확장 함수
fun String.lastChar(): Char = this.get(this.length - 1)
// fun String.lastChar(): Char = get(length - 1)
// this 생략 가능
fun main() {
println("Kotlin".lastChar())
}
>>> n
→ String: 수신 객체 타입 → 확장할 클래스
→ this: 수신 객체 → 확장 함수가 호출되는 대상이 되는 값
import strings.lastChar
fun main() {
val c = "Kotlin".lastChar()
println(c)
}
: 정적 메소드를 호출하면서 첫번째 인자로 수신 객체 넘기기
char c = StringUtilsKt.lastChar("Java");
fun main() {
val list1 = listOf(1,2,3)
println(list1.joinToString(separator = ";", prefix = "[", postfix = "]"))
val list2 = arrayListOf(1,2,3)
println(list2.joinToString(separator = ";", prefix = "[", postfix = "]"))
}
→ joinToString을 마치 클래스 멤버인거처럼 호출이 가능
오버라이딩
open class View {
open fun click() = println("View clicked")
}
// Button은 View를 확장한다.
class Button: View() {
override fun click() = println("Button clicked")
}
fun main() {
val view: View = Button()
view.click()
}
>>> Button clicked
확장 함수의 오버라이딩
open class View {
open fun click() = println("View clicked")
}
// Button은 View를 확장한다.
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")
fun main() {
val view: View = Button()
view.showOff()
}
>>> I'm a view
→ view가 가리키는 객체의 실제 타입은 Button이지만, view의 type은 View이므로 View.showOff()가 호출된다.
💡 확장 함수와 멤버 함수의 이름이 같으면? 멤버 함수의 우선 순위가 더 높아서 멤버 함수가 호출된다.
fun FunctionUtil.hello() {
println("hello-extend")
}
class FunctionUtil {
constructor()
fun hello() {
println("hello-class")
}
}
fun main() {
val func: FunctionUtil = FunctionUtil()
func.hello()
}
>>> hello-class
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!