Statically typed programming language for modern multiplatform applications (최신 멀티플랫폼 애플리케이션을 위한 정적 타입 언어)
라고 정의합니다.정적 타입 언어
입니다. 코틀린을 사용하다보면 val num = 0
처럼 타입을 생략하고 변수를 선언하는 경우가 있는데 이는 동적 타입 언어라서 그런게 아니라 추론을 통해 타입을 생략할 수 있기 때문입니다.동적 바인딩
이 가능합니다. 즉, 컴파일 시 타입이 결정되기는 하지만(정적 타입 언어) 해당 타입으로 올 수 있는 여러 타입 중 어느 타입이 사용될지는 컴파일 시 결정(동적 바인딩)됩니다.강의 영상은 유튜브에서 확인할 수 있습니다.
여기서는 간단한 예제를 통해 코틀린의 장점을 소개해 주고 있습니다.
아래 두 코드는 완전히 동일합니다.
자바 코드
package com.example.SimpleKotlinTest.introduction;
public class JavaMoney {
private int amount;
private String currency;
public JavaMoney(int amount, String currency) {
this.amount = amount;
this.currency = currency;
}
public int getAmount() {
return amount;
}
public String getCurrency() {
return currency;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
return super.equals(obj);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
return "JavaMoney{" +
"amount=" + amount +
", currency='" + currency + '\'' +
'}';
}
}
코틀린 코드
package com.example.SimpleKotlinTest.introduction
data class Money(val amount: Int, val currency: String)
val
또는 var
을 반드시 붙여줘야 합니다.코틀린 코드
package com.example.SimpleKotlinTest.introduction
fun main(args: Array<String>) {
val tickets = Money(100, "$")
val popcorn1 = tickets.copy(500, "EUR")
var popcorn2 = tickets.copy(100, "$")
if (tickets != popcorn1) {
println("popcorn1 is different!")
}
if (tickets != popcorn2) {
println("popcorn2 is different!")
}
}
data class Money(val amount: Int, val currency: String)
new생성자
를 사용하지 않습니다.val popcorn1: Money = tickets.copy(500, "EUR")
)==
비교를 통과합니다. 코틀린에서는 ==
연산자로 값이 같은지 비교하고, ===
연산자가 참조값이 같은지를 비교합니다. val tickets = Money(100, "$")
val popcorn1: Money = tickets.copy(500, "EUR")
var popcorn2 = tickets.copy(100, "$")
println(popcorn1 == tickets); // false
println(popcorn2 == tickets); // true
println(popcorn1 === tickets); // false
println(popcorn2 === tickets); // false
Java로 작성한 클래스를 Kotlin에서 사용할 수 있고, Kotlin으로 작성한 클래스를 Java에서 사용할 수 있습니다.
코틀린 코드
package com.example.SimpleKotlinTest.introduction
fun main(args: Array<String>) {
val javaMoney = JavaMoney(100, "$")
println("money : ${javaMoney.amount}, currency : ${javaMoney.currency}")
}
.변수명
으로 호출합니다.코틀린 코드와 자바 코드를 호환해서 사용하다보면 플랫폼 타입
과 관련된 문제가 발생할 수 있습니다. 플랫폼 타입이란 코틀린이 null과 관련된 정보를 알 수 없는 타입
을 말합니다. 자바에서 @Nullable
과 @NotNull
을 모두 선언하지 않은 변수를 코틀린에서 사용할 때는 해당 변수가 null이 허용되는지 아닌지를 잘 확인하고 써야 합니다.
코틀린에서는 함수를 fun 함수 이름(파라미터 이름: 타입, …) : 리턴 타입
형식으로 선언합니다. 자바의 void와 유사한 타입은 코틀린에서 Unit
이 있습니다. Unit은 아무것도 반환하지 않는 반환 타입을 말합니다. 리턴 타입을 생략하면 Unit
으로 간주합니다.
코틀린 코드
fun sendPayment(money: Money, message: String = "") {
println("Sending ${money.amount}")
}
fun main(args: Array<String>) {
val tickets = Money(100, "$")
val popcorn = tickets.copy(500, "EUR")
sendPayment(tickets)
sendPayment(message = "Good luck!!", money = popcorn)
}
money, message
순서로 선언했지만 main에서 message,money
로 값을 넣어주고 있습니다.코틀린에서는 이미 존재하는 클래스에 함수를 덧붙일 수 있습니다.
그러헌 함수를 바로 확장 함수
라고 부릅니다.
코틀린 코드
import java.math.BigDecimal
fun main(args: Array<String>) {
val bd = BigDecimal(100)
val popcorn = Money(100.bd, "$")
bd.percent(7)
7.percentOf(popcorn)
}
data class Money(val amount: BigDecimal, val currency: String)
fun sum(x: Int, y: Int) = x + y
fun BigDecimal.percent(percentage : Int) = this.multiply(BigDecimal(percentage)).divide(BigDecimal(100))
fun Int.percentOf(money: Money) = money.amount.multiply(BigDecimal(this)).divide(BigDecimal(100))
val Int.bd: BigDecimal
get() = BigDecimal(this)
bd.percent(7)
와 같은 사용이 가능해졌습니다.코틀린 코드
fun convertToDollars(money: Money) : Money {
when (money.currency) {
"$" -> return money
"EUR" -> return Money(money.amount * BigDecimal(1.10), "$")
else -> throw IllegalArgumentException("not the currency you're interested in!")
}
}
fun convertToDollars(money: Money) : Money {
return when (money.currency) {
"$" -> money
"EUR" -> Money(money.amount * BigDecimal(1.10), "$")
else -> throw IllegalArgumentException("not the currency you're interested in!")
}
}
fun convertToDollars(money: Money) = when (money.currency) {
"$" -> money
"EUR" -> Money(money.amount * BigDecimal(1.10), "$")
else -> throw IllegalArgumentException("not the currency you're interested in!")
}
:
대신 = 함수
를 활용할 수도 있습니다. 코틀린에서의 함수는 1급객체이기 때문에 이러한 문법이 가능합니다.기본 연산자를 클래스에서 사용할 수 있게 연산자를 정의할 수 있습니다.
코틀린 코드
import java.math.BigDecimal
fun main(args: Array<String>) {
val tickets = Money(BigDecimal(100), "$")
val popcorn = tickets.copy(BigDecimal(500), "EUR")
val cost = tickets + popcorn
}
data class Money(val amount: BigDecimal, val currency: String)
operator fun Money.plus(money: Money) =
if (currency == money.currency) {
Money(amount + money.amount, currency)
} else {
throw IllegalArgumentException("We're gonna have a problem here!")
}
plus
를 오버로딩했기 때문에 Money타입의 tickets와 popcorn을 +
로 연산할 수 있습니다.코틀린에서는 null을 사용하기 위해서는 변수나 파라미터가 null일 수 있다는 걸 명시적으로 표현해야 합니다. 코틀린은 명시적으로 표현하지 않은 곳에서는 null을 입력할 수 없게 만들어 NPE오류를 피할 수 있게 되었습니다.
코틀린 코드
fun main(args: Array<String>) {
var train : Money = Money(BigDecimal(100), "$")
train = null // compilation error
var bus : Money? = Money(BigDecimal(50), "$")
money(bus) // compilation error
bus = null // ok
}
data class Money(val amount: BigDecimal, val currency: String)
fun money(money : Money) {
println("${money.amount} is valid.")
}
fun javaMoney(money : JavaMoney?) {
// first
if (money != null) {
println("${money.amount} is valid.")
}
// second
println("${money?.amount} is valid.")
}
타입?
을 선언해야 합니다. 타입
과 타입?
은 서로 다르게 취급됩니다.(Int
와 Int?
는 서로 다른 타입입니다.)Money
로 null을 허용하지 않습니다. 그렇기 때문에 train에 null을 넣으려고 하면 에러가 발생합니다.Money?
로 null을 허용합니다. 그렇기 때문에 bus에 null을 넣는건 가능합니다. 하지만 Money
타입을 받는 money함수에 인자로 넘기는 건 불가능합니다.Money
로 선언함으로써 null을 들어올 수 없게 했습니다. 하지만 Java와 함께 사용할 때 자바코드의 실행 결과는 null을 가질 수 있기 때문에 javaMoney함수처럼 null을 대비해야 합니다.코틀린에서의 함수는 함수를 파라미터로 받거나 함수를 결과로 반환할 수 있는
고차원 함수입니다.
코틀린 코드
data class User(val id: Int, val username: String, val email: String, val role: Role)
enum class Role {
Admin,
Regular
}
fun usersFromJSONFile(fileName: String) : List<User> {
TODO("LATER")
}
fun findEmails(users: List<User>, predicate: (String) -> (Boolean)) : List<User> {
TODO("LATER")
}
String을 입력변수로 받고 Boolean을 결과로 반환하는 함수
를 인자로 받습니다.fun main(args: Array<String>) {
val users = usersFromJSONFile("users.json")
// 01
findEmails(users, { value -> value.endsWith(".com") })
// 02
findEmails(users, { it.endsWith(".com") })
// 03
findEmails(users) {
it.endsWith(".com")
}
}
{}
로 감싸서 전달하고 있습니다.it
으로 대체할 수 있습니다.해당 클래스를 상속이 가능하게 만들려면 open
키워드나 sealed
키워드를 사용해야 합니다. open키워드는 어디에서든 상속 가능한 클래스라는 의미이고, sealed키워드는 해당 피알에서 작성된 클래스만 상속이 가능한 클래스라는 의미입니다.
코틀린 코드
sealed class UserResult
data class Success(val users: List<User>): UserResult()
data class Failure(val message: String): UserResult()
:
를 사용합니다.