val customers =10println('There are $customers customers') // There are 10 customersprintln('There are ${customers +1} customers') // There are ${customers + 1} customers
문자열 템플릿을 사용하면, 변수 내용을 표준 출력으로 내보내기 편리함
템플릿 표현식을 사용해 변수 및 기타 객체에 저장된 데이터 액세스하고, 이를 문자열로 변환 가능
val shapes:MutableList<String> =mutableListOf("triangle", "square", "circle")
val shapesLocked:List<String> = shapes
원치 않는 수정을 방지하기 위해 mutableList를 list에 할당해서 read-only를 얻을 수 있음
Set
// Read-only setval readOnlyFruit =setOf("apple", "banana", "cherry", "cherry")
// Mutable set with explicit type declarationval fruit:MutableSet<String> =mutableSetOf("apple", "banana", "cherry", "cherry")
println(readOnlyFruit) // [apple, banana, cherry]val fruitLocked:Set<String> = fruit
println("This set has ${readOnlyFruit.count()} items") // This set has 3 items
fruit.add("dragonfruit")
println(fruit) // [apple, banana, cherry, dragonfruit]
fruit.remove("dragonfruit")
println(fruit) // [apple, banana, cherry]
정렬되지 않고, 고유 항목만 저장
세트는 순서가 지정되지 않으므로, 특정 인덱스 항목에 액세스 불가
세트를 생성할 때 Kotlin은 저장된 항목의 유형을 유추 가능
원치 않는 변경을 방지하기 위해서 캐스팅 사용 가능
setOf() 읽기 전용 세트 생성
mutableSetOf() 변경 가능한 세트 생성
.count() 세트의 항목 개수 반환
in 세트가 해당 아이템을 가지고 있는지 검사하는 연산자
.add(), .remove() 세트에 아이템 추가/삭제
Map
// Read-only mapval readOnlyJuiceMenu =mapOf("apple" to 100, "kiwi" to 190, "orange" to "100")
println(readOnlyJuiceMenu) // {apple=100, kiwi=190, orange=100}// Mutable map with explicit type declarationval juiceMenu =MutableMap<String, Int> =mutableMapOf("apple" to 100, "kiwi" to 190, "orange" to 100)
println(juiceMenu) // {apple=100, kiwi=190, orange=100}// Prevent unwanted modifications, obtain read-only views of mutable maps by castingval juiceMenuLocked:Map<String, Int> = juiceMenu
println(readOnlyJuiceMenu["apple"]) // 100println(readOnlyJuiceMenu.count()) // 3
juiceMenu.put("coconut", 150)
println(juiceMenu) // {apple=100, kiwi=190, orange=100, coconut=150}
juiceMenu.remove("orange")
println(juiceMenu) // {apple=100, kiwi=190, coconut=150}println(readOnlyJuiceMenu.containsKey("kiwi")) // trueprintln(readOnlyJuiceMenu.keys) // [apple, kiwi, orange]println(readOnlyJuiceMenu.value) // [100, 190, 100]println("orange"in readOnlyJuiceMenu.keys) // trueprintln(200in readOnlyJuiceMenu.values) // false
항목을 키-값 쌍으로 저장
키를 참조하여 값에 액세스
맵을 생성할 때 Kotlin은 아이템의 타입 추론
mapOf() 읽기 전용 맵 생성
mutableMapOf() 변경 가능한 맵 생성
[key] **to** [value]
map[key] // value
.count() 맵에 저장된 항목의 수
.put(), .remove() 맵에 항목을 추가/제거
.containsKey() 해당 키가 맵에 존재하는지 여부
.keys, .values 맵에 존재하는 키/값 목록을 리스트로 반환
.keys, .values 는 객체의 프로퍼티
Control Flow
Conditional Expressions
if
val d:Intval check:Boolean=trueif (check) {
d =1
} else {
d =2
}
println(d) // 1val a =1val b =2
pinrtln(if (a > b) a else b) // 2
when
val obj ="Hello"when (obj) {
// Check whether obj equals to "1""1"->println("One")
// Check whether obj equals to "Hello""Hello"->println("Greeting")
// Default statementelse->println("Unknown")
} // Greetingval result =when (obj) {
// If obj equals "1", sets result to "one""1"->"One"// If obj equals "Hello", sets result to "Greeting""Hello"->"Greeting"// Sets result to "Unknown" if no previous condition is satisfiedelse->"Unknown"
}
println(result) // Greetingval temp =18val description =when {
temp <0->"very cold"
temp <10->"a bit cold"
temp <20->"warm"else->"hot"
}
println(description) // warm
Ranges
1..4 1, 2, 3, 4
1..<4 1, 2, 3
4 downTo 1 4, 3, 2, 1
1..5 step 2 1, 3, 5
'a'..'d' 'a', 'b', 'c', 'd'
'z' downTo 's' step 2 'z', 'x', 'v', 't'
Loops
// for for (number in1..5) {
// number is the iterator and 1..5 is the rangeprint(number)
} // 12345val cakes =listOf("carrot", "cheese", "chocolate")
for (cake in cakes) {
println("Yummy, it's a $cake cake!")
}
// Yummy, it's a carrot cake!// Yummy, it's a cheese cake!// Yummy, it's a chocolate cake!// whilevar cakesEaten =0while (cakesEaten <3) {
println("Eat a cake")
cakesEaten++
}
// Eat a cake// Eat a cake// Eat a cakevar cakesBaked:Int=0do {
pritnln("Bake a cake")
cakesBaked++
} while (cakesBaked < cakesEaten)
// Bake a cake// Bake a cake// Bake a cake
Functions
funhello() {
returnprintln("Hello, world!")
}
hello() // Hello, world!funsum(x:Int, y:Int): Unt {
return x + y
}
println(sum(1, 2)) // 3
Named arguments
funprintMessageWithPrefix(message:String, prefix:String) {
println("[$prefix] $message")
}
funmain() {
// Use named argumnets with swapped parameter order
printMessageWithPrefix(prefix ="Log", message ="Hello")
// [Log] Hello
}
val upperCaseString = { text:String-> text.uppercase() }
println(upperCaseString("hello")) // HELLO
Pass to another function
val numbers:List<Int> =listOf(1, -2, 3, -4, 5, -6)
val positives = numbers.filter { x -> x >0 }
val negatives = numbers.filter { x -> x <0 }
println(positives) // [1, 3, 5]println(negatives) // [-2, -4, -6]val doubled = numbers.map { x -> x *2 }
val tripled = numbers.map { x -> x *3 }
println(doubled) // [2, -4, 6, -8, 10, -12]println(tripled) // [3, -6, 9, -12, 15, -18]
Return from a function
funtoSeconds(time:String): (Int) ->Int=when (time) {
"hour"-> { value -> value *60.pow() }
"minute"-> { value -> value *60 }
"second"-> { value -> value }
else-> { value -> value }
}
val timesInMinutes:List<Int> =listOf(2, 10, 15, 1)
val min2sec = toSeconds("minute")
val totalTimeInSeconds = timesInMinutes.map(min2Sec).sum()
println("Total tiem is $totalTimeInSeconds secs") // Total time is 1680 secs
// The initial value is zero.// The operation sums the initial value with every item in the list cumulatively.println(listOf(1, 2, 3).fold(0, { x, item -> x + item })) // 6// Alternatively, in the form of a trailing lambdaprintln(listOf(1, 2, 3).fold(0) { x, item -> x + item }) // 6
람다 표현식이 유일한 함수 매개 변수인 경우, 함수 괄호를 삭제 가능
람다 표현식이 함수의 마지막 매개 변수로 전달되는 경우, 함수 괄호 밖에 표현식 작성 가능
이를 후행 람다라고 함
Classes
classCustomer
Properties
classContact(valid:Int, varemail:String = "example@gmail.com") {
val category:String="work}
클래스 인스턴스가 생성된 후 변경해야 하는 경우가 아니라면, 프로퍼티를 read-only(val)로 선언하는 것이 좋음
괄호 안에 val, var 없이 프로퍼티를 선언할 수 있지만, 인스턴스가 생성된 후에는 이러한 프로퍼티에 액세스 불가
classContact(valid:Int, varemail:String)
funmain() {
val contact =Contact(1, "mary@gmail.com")
// Prints the value of property: emailprintln(contact.email) // mary@gmail.com// Updates the value of the property: email
contact.email ="jane@gmail.com"// Prints the new value of the property: emailprintln(contact.email)
// jane@gmail.comprintln("Their email address is: ${contact.email}")
}
Member functions
classContact (valid:Int, varemail:String) {
funprintId() {
println(id)
}
}
funmain() {
val contact =Contact(1, "mary@gmail.com")
// Calls member function printId()
contact.printId()
// 1
}
객체 특성의 일부로 프로퍼티를 선언하는 것 외에도 멤버 함수를 사용해 객체의 동작을 정의할 수 있음
Data classes
data classUser(valname:String, valid:Int)
Functions
Description
.toString()
클래스 인스턴스와 그 프로퍼티를 문자열로 출력
.equals() or ==
클래스의 인스턴스 비교
.copy()
다른 속성을 가진 클래스 인스턴스를 복사해 클래스 인스턴스 생성
데이터 클래스는 클래스와 동일한 기능을 갖지만, 멤버 함수가 자동으로 제공됨
이러한 함수는 자동으로 사용할 수 있으므로 보일러플레이트를 작성할 필요가 없음
Print as string
val user =User("Alex", 1)
// Automatically uses toString() function so that output is easy to readprintln(user) // User(name=Alex, id=1)
Compare instances
val user =User("Alex", 1)
val secondUser =User("Alex", 1)
val thirdUser =User("Max", 2)
// Compares user to second userprintln("user === secondUser: ${user == secondUser}") // user == secondUser: true// Compares user to third userprintln("user == thirdUser: ${user == thirdUser}") // user == thirdUser: false
Copy instance
val user =User("Alex", 1)
val secondUser =User("Alex", 1)
val thirdUser =User("Max", 2)
// Create an exact copy of userprintln(user.copy()) // User(name=Alex, id=1)// Creates a copy of user with name: "Max"println(user.copy("Max")) // User(name=Max, id=1)// Creates a copy of user with id: 3println(user.copy(id =3)) // User(name=Alex, id=3)
데이터 클래스 인스턴스의 정확한 복사본을 만들려면 인스턴스에서 .copy() 함수를 호출
데이터 클래스 인스턴스의 복사본을 만들고 일부 속성을 변경하려면, 인스턴스 .copy() 함수를 호출하고 속성의 대체 값을 함수 매개 변수로 추가
인스턴스의 복사본을 만드는 것이, 원본 인스턴스를 수정하는 것보다 안전함
원본 인스턴스에 의존하는 모든 코드는 복사본과 복사본으로 수행하는 작업의 영향을 받지 않기 때문
프로그램에서 null값으로 인한 문제를 방지하기 위해 Kotlin에는 null safety 기능 존재
null safety는 런타임이 아닌, 컴파일 시점에 null 값의 잠재적 문제를 감지함
Nullable types
funmain() {
// neverNull has String typevar neverNull:String="This can't be null"// Throws a compiler error
neverNull =null// nullable has nullable String typevar nullable:String?="You can keep a null here"// This is OK
nullable =null// By default, null values aren't acceptedvar inferredNonNull ="The compiler assumes non-nullable"// Throws a compiler error
inferredNonNull =null// notNull does not accept null valuesfunstrLength(notNull:String): Int {
return notNull.length
}
println(strLength(neverNull)) // 18println(strLength(nullable)) // Throws a compiler error
}
Kotlin은 선언된 타입이 null 값을 가질 가능성을 허용하는 nullable 타입을 지원
기존적으로 타입은 null 값을 허용하지 않음
nullable 타입은 타입 선언 뒤에 명시적으로 ?를 추가하여 선언
Check for null values
fundescribeString(maybeString:String?): String {
if (maybeString !=null&& maybeString.length >0) {
return"String of length ${maybeString.length}"
} else {
return"Empty or null string"
}
}
funmain() {
val nullString:String?=nullprintln(describeString(nullString)) // Empty of null string
}