작성완료: 2022-02-25
package org.kotlinlang.play // 1
fun main() { // 2
println("Hello, World!") // 3
}
main 함수이다. 코틀린 1.3부터 main을 파라미터 없이 선언할 수 있다. 리턴 타입은 지정되지 않았다. 이는 이 함수가 아무것도 리턴하지 않는다는 것을 뜻한다. println은 스탠더드 아웃풋에 문장을 쓴다. 이것은 암묵적으로 import되어 있다. 세미콜론 또한 선택적이다. 1.3 이전의 코틀린에서는 main 함수는 반드시 Array<String> 함수를 가져야 했다.
fun main(args: Array<String>) {
println("Hello, World!")
}
fun printMessage(message: String): Unit { // 1
println(message)
}
fun printMessageWithPrefix(message: String, prefix: String = "Info") { // 2
println("[$prefix] $message")
}
fun sum(x: Int, y: Int): Int { // 3
return x + y
}
fun multiply(x: Int, y: Int) = x * y // 4
fun main() {
printMessage("Hello") // 5
printMessageWithPrefix("Hello", "Log") // 6
printMessageWithPrefix("Hello") // 7
printMessageWithPrefix(prefix = "Log", message = "Hello") // 8
println(sum(1, 2)) // 9
println(multiply(2, 4)) // 10
}
String 파라미터를 가지고 Unit을 리턴하는 단순한 함수 (즉, 값을 리턴하지 않는다).info를 기본 값으로 가진 두번째 선택적 파라미터가 있는 함수. 리턴 타입은 생략되었고, 이는 타입이 실제로는 Unit임을 뜻한다.Int를 리턴하는 함수Int를 반환하는 단일 표현식 함수단일 파라미터를 가진 멤버 변수와 표현식은 infix 함수로 바뀔 수 있다.
fun main() {
infix fun Int.times(str: String) = str.repeat(this) // 1
println(2 times "Bye ") // 2
val pair = "Ferrari" to "Katrina" // 3
println(pair)
infix fun String.onto(other: String) = Pair(this, other) // 4
val myPair = "McLaren" onto "Lucas"
println(myPair)
val sophia = Person("Sophia")
val claudia = Person("Claudia")
sophia likes claudia // 5
}
class Person(val name: String) {
val likedPeople = mutableListOf<Person>()
infix fun likes(other: Person) { likedPeople.add(other) } // 6
}
Int에 infix 표현식 함수를 정의한다.to infix 함수를 호출해 Pair를 생성한다.onto라고 불리는 당신만의 to 구현.예시는 지역변수(다른 함수 내에 중첩된 함수)를 사용한다는 점을 주의하자.
특정 함수들은 operator 심볼로 호출함으로써 연산자로 '업그레이드' 될 수 있다.
operator fun Int.times(str: String) = str.repeat(this) // 1
println(2 * "Bye ") // 2
operator fun String.get(range: IntRange) = substring(range) // 3
val str = "Always forgive your enemies; nothing annoys them so much."
println(str[0..14])
// operator fun Int.times(str: String) = "- $str"
// println(2 * "Bye") 결과는 - Bye
operator modifier(수식자/제한자))를 사용해 위 infix 함수를 한 단계 더 발전시킨다.times에 대한 연산자 심볼은 *이므로 2 * "Bye"를 사용해 호출할 수 있다. get() 연산자는 대괄호 엑세스 구문으로 활성화된다.vararg 파라미터가 있는 함수Varargs는 아규먼트들을 콤마로 나눔으로써 어떤 수의 아규먼트든 전달할 수 있게 해 준다.
fun printAll(vararg messages: String) { // 1
for (m in messages) println(m)
}
printAll("Hello", "Hallo", "Salut", "Hola", "你好") // 2
fun printAllWithPrefix(vararg messages: String, prefix: String) { // 3
for (m in messages) println(prefix + m)
}
printAllWithPrefix(
"Hello", "Hallo", "Salut", "Hola", "你好",
prefix = "Greeting: " // 4
)
fun log(vararg entries: String) {
printAll(*entries) // 5
// printAll(entries) //Type mismatch : inferred type is Array<out String> but String was expected
}
vargar modifier는 파라미터를 vararg로 바꾼다.printAll을 어떤 수의 문자열 아규먼트를 사용해서든 호출할 수 있도록 한다.prefix에 값을 설정할 수 있다.*를 사용한다. 이를 통해 entries (Array< String >) 대신 *entries (String vararg)를 전달할 수 있다.코틀린은 강력한 타입 추론을 가지고 있다. 변수의 타입을 명시적으로 선언할 수 있지만, 일반적으로는 컴파일러가 추론을 통해 그 작업을 하도록 두게 될 것이다.
(불변성이) 권장되기는 하지만 코틀린은 불변성(immutability)를 강제하지 않는다. 기본적으로 var 보다 val을 사용하도록 하자.
var a: String = "initial" // 1
println(a)
val b: Int = 1 // 2
val c = 3 // 3
Int를 추론한다.var e: Int // 1
println(e) // variable 'e' must be initialized // 2
언제 변수를 초기화할지 자유롭게 선택할 수 있다. 하지만 처음 읽히기 전에는 반드시 초기화되어야 한다.
val d: Int // 1
if (someCondition()) {
d = 1 // 2
} else {
d = 2 // 2
}
println(d) // 3
NullPointerException 세계를 지우기 위한 노력으로 코틀린의 변수 타입은 null할당을 허용하지 않는다. 변수가 null이 될 필요가 있다면 ?를 타입 마지막에 붙여 nullable을 선언해야 한다.
var neverNull: String = "This can't be null" // 1
// Null can not be a value of a non-null type String
neverNull = null // 2
var nullable: String? = "You can keep a null here" // 3
nullable = null // 4
var inferredNonNull = "The compiler assumes non-null" // 5
// Null can not be a value of a non-null type String
inferredNonNull = null // 6
fun strLength(notNull: String): Int { // 7
return notNull.length
}
strLength(neverNull) // 8
// Type mismatch: inferred type is Nothing? but String was expected
strLength(nullable) // 9
null을 non-nullable 변수에 할당하려고 시도하면 컴파일 오류가 생성된다.null 을 nullable variable에 설정할 수 있다.null을 할당하려고 하면 컴파일 오류가 발생한다.가끔씩 코틀린 프로그램은 null 값으로 작업해야 할 경우가 있다. 예를 들어 외부 자바 코드와 상호작용할 때나, 존재하지 않는 상태(truly absent state)를 표현해야 할 때 그렇다. 코틀린은 이런 사왕을 우아하게 다루기 위한 null tracking을 제공한다.
fun describeString(maybeString: String?): String { // 1
if (maybeString != null && maybeString.length > 0) { // 2
return "String of length ${maybeString.length}"
} else {
return "Empty or null string" // 3
}
}
클래스 선언은 클래스 이름, 클래스 헤더(타입 파라미터, 기본 생성자 등을 지정한다), 그리고 중괄호로 둘러싸인 클래스 바디로 구성된다. 해더와 바디는 선택적이다. 만약 클래스에 바디가 없다면 중괄호는 생략될 수 있다.
class Customer // 1
class Contact(val id: Int, var email: String) // 2
fun main() {
val customer = Customer() // 3
val contact = Contact(1, "mary@gmail.com") // 4
println(contact.id) // 5
contact.email = "jane@gmail.com" // 6
}
Customer라는 클래스를 프로퍼티나 유저가 정의한 생성자 없이 선언한다. 코틀린에 의해 파라미터 없는 기본 생성자가 자동으로 생성된다.id와 가변 변수 email)와 두 파라미터 id, email을 사용하는 생성자로 클래스를 선언한다. Customer 클래스의 인스턴스를 생성한다. 코틀린에는 new 키워드가 없다.Contact 클래스의 인스턴스를 생성한다.id에 엑세스한다.email의 값을 업데이트한다.제네릭은 모던 랭귀지에서 표준이 된 일반성 메커니즘(genericity mechanism)이다. 제네릭 클래스와 함수들은 특정한 제너릭 타입과 독립적인 common logic을 캡슐화함으로써 코드 재사용성을 높인다. 한 예로 List<T> 내부 로직은 T가 무엇인지에 독립적이다.
코틀린에서 제네릭을 사용하는 첫 번째 방법은 제네릭 클래스를 생성하는 것이다.
class MutableStack<E>(vararg items: E) { // 1
private val elements = items.toMutableList()
fun push(element: E) = elements.add(element) // 2
fun peek(): E = elements.last() // 3
fun pop(): E = elements.removeAt(elements.size - 1)
fun isEmpty() = elements.isEmpty()
fun size() = elements.size
override fun toString() = "MutableStack(${elements.joinToString()})"
}
MutableStack<E>를 정의한다. E는 제네릭 타입 파라미터라고 불린다. 사용 현장에서는 특정한 타입으로 할당된다. 예: MutableStack<Int>를 선언함으로써 Int할당.E는 다른 타입들과 마찬가지로 파라미터로 사용될 수 있다.E를 사용할 수 있다.구현에는 단일 표현식으로 정의될 수 있는 코틀린의 약식 구문을 많이 사용한다.
로직이 특정 타입과 독립적인 함수를 제네릭으로 생성해 사용할 수 있다. 예를 들어 가변 스택을 생성하는 유틸리티 함수를 작성할 수 있다:
fun <E> mutableStackOf(vararg elements: E) = MutableStack(*elements)
fun main() {
val stack = mutableStackOf(0.62, 3.14, 2.7)
println(stack)
}
컴파일러는 mutableStackOf의 파라미터들에서 제네릭 타입을 추론할 수 있다. 그러므로 mutableStackOf<Double>(...)라고 작성할 필요는 없다.
코틀린은 전통적인 객체 지향 상속 메커니즘을 완전히 지원한다.
open class Dog { // 1
open fun sayHello() { // 2
println("wow wow!")
}
}
class Yorkshire : Dog() { // 3
override fun sayHello() { // 4
println("wif wif!")
}
}
fun main() {
val dog: Dog = Yorkshire()
dog.sayHello()
}
final이다. 클래스 상속을 허용하고 싶을 경우 open 제한자로 클래스를 표시한다.final이다. 클래스와 마찬가지로, open 제한자를 사용하면 오버라이딩을 허용한다.: SuperclassName()을 지정하면 superclass를 상속한다. 빈 괄호 ()는 superclass default constructor의 호출을 가리킨다.override 제한자를 필요로 한다.open class Tiger(val origin: String) {
fun sayHello() {
println("A tiger from $origin says: grrhhh!")
}
}
class SiberianTiger : Tiger("Siberia") // 1
fun main() {
val tiger: Tiger = SiberianTiger()
tiger.sayHello()
}
open class Lion(val name: String, val origin: String) {
fun sayHello() {
println("$name, the lion from $origin says: graoh!")
}
}
class Asiatic(name: String) : Lion(name = name, origin = "India") // 1
fun main() {
val lion: Lion = Asiatic("Rufo") // 2
lion.sayHello()
}
Asiatic 선언에서 name은 val도 var도 아니다. 이것은 생성자 아규먼트로, 그 값은 슈퍼클래스 Lion의 name 프로퍼티로 전달된다.Rufo로 Asiatic 인스턴스를 생성한다. 콜은 Rufo와 India 아규먼트를 가진 Lion 생성자를 호출한다.