// val 변수명: 타입 = 값
val a: Int = 1
// 타입 생략 가능
val b = 1
// 타입이 고정되면 다른 타입으로 할당 불가
var c = 123 // Int
c = "test" // 불가능
🧨 탑 레벨에도 변수 할당이 가능
var x: Int = 5 fun main() { x += 1 println(x) }
- 실행
val d: Int
d = 1
// 기본적인 함수 선언 스타일
fun sum(a: Int, b: Int) : Int {
return a + b
}
// 표현식 스타일
fun sum2(a: Int, b: Int) : Int = a + b
// 표현식 & 반환타입 생략
fun sum3(a: Int, b: Int) = a + b
// 몸통이 있는 함수는 반환 타입 생략 불가
fun sum4(a: Int, b: Int) : Int {
return a + b
}
// 반환타입이 없는 함수는 Unit 을 반환 (생략가능)
fun printSum(a: Int, b: Int) : Unit {
println("$a + $b = ${a + b}")
}
fun main() {
greeting()
greeting("Hi!!!")
}
fun greeting(message: String = "Hello World!") {
println(message)
}
fun main() {
welcome("Hi", "bobby")
welcome(name = "bobby")
welcome(message = "Hi", name = "bobby")
welcome(name = "bobby", message = "Hey")
welcome(message = "Hi", "bobby")
}
fun welcome(message: String = "Welcome", name: String) {
println("$message, $name!!")
}
fun main() {
// if...else
val age: Int = 20
if (age < 20) {
println("학생입니다.")
} else {
println("성인입니다.")
}
}
fun main()
// 코틀린의 if...else 는 값을 리턴할 수 있다.
val score: Int = 80
val message = if (score >= 80) {
"합격"
} else {
"불합격"
}
println(message)
}
fun main() {
// when ... else
val day = 2
val result = when (day) {
1 -> "월요일"
2 -> "화요일"
3 -> "수요일"
4 -> "목요일"
5 -> "금요일"
6 -> "토요일"
7 -> "일요일"
else -> "잘못입력"
}
println(result)
}
fun main() {
val day = Day.SATURDAY
when(day) {
Day.MONDAY -> println("월요일")
Day.TUESDAY -> println("화요일")
Day.WEDNESDAY -> println("수요일")
Day.THURSDAY -> println("목요일")
Day.FRIDAY -> println("금요일")
Day.SATURDAY -> println("토요일")
Day.SUNDAY -> println("일요일")
}
}
enum class Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
fun main() {
val grade = "A"
when(grade) {
"A", "B", "C" -> println("합격")
else -> println("불합격")
}
}
fun main() {
// 0 <= i <= 2
for (i in 0..2) {
println(i)
}
}
fun main() {
// 0 <= i < 3
for (i in 0 until 2) {
println(i)
}
}
fun main() {
for (i in 0..4 step 2) {
println(i)
}
}
fun main() {
for (i in 3 downTo 1) {
println(i)
}
}
fun main() {
val numbers = arrayOf(1, 2, 3)
for (i in numbers) {
println(i)
}
}
fun main() {
var x = 3
while (x > 0) {
println(x)
x--
}
}
fun main() {
val a: String = null // 컴파일 에러
var b: String = "asdf"
b = null // 컴파일 에러
}
fun main() {
val a: String? = null
println(a?.length)
val b: Int = if (a != null) a.length else 0
println(b)
// 엘비스 연산자 (값이 null일 경우 반환 값 지정)
val c = a?.length ?: 0
println(c)
}
fun getNullStr(): String? = null
fun getLengthIfNotNull(str: String?) = str?.length ?: 0
fun main() {
val nullableStr = getNullStr()
val nullableStrLength = nullableStr?.length ?: 0
println(nullableStrLength)
val length = getLengthIfNotNull(null)
println(length)
}
NullPointerException 이 발생하는 경우
- 직접 에러 throw
throw NullPointerException()
- 단언연산자 사용 후 null이 들어왔을 때
val c: String? = null val d = c!!.length
- 자바 코드와 같이 사용할 때 자바 코드에서 null이 발생하는 경우
fun main() {
Thread.sleep(1) // (O)
}
fun main() {
try {
throw RuntimeException()
} catch (e: Exception) {
println("에러 발생시 수행")
} finally {
println("finally 실행")
}
}
fun main() {
val a = try {
"test".length
} catch (e: Exception) {
println("에러 발생시 수행")
}
println(a)
}
fun main() {
val a: String? = null
val b = a ?: fail("a is null")
println(b.length)
}
fun fail(message: String): Nothing {
throw IllegalArgumentException(message);
}
참고
fun main() { val a: String? = null val b: String = a ?: fail("a is null") println(b.length) // b 가 null인 경우 위에서 예외가 발생하기 때문에 nullable 하지 않아도 컴파일 에러가 발생하지 않음. } fun fail(message: String): Nothing { throw IllegalArgumentException(message); }
class
키워드를 사용하며 내용이 없는 빈 클래스도 생성이 가능class Coffee {
}
class EmptyClass
constructor
키워드를 사용하며 생략 가능class Coffee constructor(val name:String) {
}
// 생략
class Coffee(val name:String) {
}
// 기본값 지정
class Coffee (
val name: String = "",
// 기본값 지정
val price: Int = 0,
) {
}
fun main() {
val coffee = Coffee();
coffee.name = "아이스 아메리카노"
coffee.price = 2000
println("${coffee.name} 가격은 ${coffee.price}원")
}
class Coffee (
var name: String = "", // 기본값 지정
var price: Int = 0,
) {
val brand: String
// getter 직접 생성
get() {
return "스타벅스"
}
var quantity: Int = 0
// setter 직접 생성
set(value) {
if (value > 0) {
field = value // 필드에 접근하여 데이터 저장
}
}
}
fun main() {
val coffee = Coffee();
coffee.name = "아이스 아메리카노"
coffee.price = 2000
coffee.quantity = 1
println("${coffee.brand}: ${coffee.name} ${coffee.quantity}개 가격은 ${coffee.price}원")
}
🧨 field를 사용하는 이유
coffee.quantity = 1 // 1. 사용시 setter 호출하고
var quantity: Int = 0 set(value) { if (value > 0) { quantity = value // 2. setter 내부에서 다시 setter 호출 } }
무한으로 setter를 호출 하여 StackOverFlow 발생
Any
클래스를 상속받고 있다.open
키워드를 사용해 상속이 가능하게 할 수 있다.open
키워드를 사용해 하위 클래스에서 재정의 할 수 있다.open class Dog {
open var age: Int = 0
open fun bark() {
println("멍멍")
}
}
class Bulldog: Dog() {
override var age: Int = 0
override fun bark() {
println("컹컹")
}
}
class Bulldog(override var age: Int = 0): Dog() {
override fun bark() {
println("컹컹")
}
}
open
이다.final
키워드를 사용한다.class Bulldog(final override var age: Int = 0): Dog() {
final override fun bark() {
println("컹컹")
}
}
super
키워드 사용 override fun bark() {
super.bark()
}
fun main() {
val dog = Bulldog(age = 2)
println(dog.age)
dog.bark()
}
abstract
키워드를 사용하여 추상 클래스를 생성할 수 있다.abstract class Developer {
abstract var age: Int
abstract fun code(language: String)
}
class Backend(override var age: Int = 0): Developer() {
override fun code(language: String) {
println("I code with $language")
}
}
fun main() {
val backendDeveloper = Backend(age = 20)
println(backendDeveloper.age)
backendDeveloper.code("Kotlin")
}
class Product(val name: String, val price: Int)
interface Cart {
// 추상 프로퍼티
val coin: Int
val weight: String
get() = "20kg" // backing field 사용 불가
// 추상 함수
fun add(product: Product)
// 구현이 있는 함수
fun rent() {
if (coin > 0) {
println("카트를 대여합니다.")
}
}
}
class MyCart(override val coin: Int) : Cart {
override fun add(product: Product) {
if (coin <= 0) println("코인을 넣어주세요.")
else println("${product.name}이(가) 카트에 추가됐습니다.")
}
}
interface Wheel {
fun roll()
}
interface Cart: Wheel {
...
override fun roll() {
println("카트가 굴러갑니다.")
}
}
fun main() {
val cart = MyCart(coin = 100)
cart.rent()
cart.roll()
cart.add(Product(name = "장난감", price = 1000))
}
interface Order {
fun add(product: Product) {
println("${product.name} 주문이 완료되었습니다.")
}
}
class MyCart(override val coin: Int) : Cart, Order {
override fun add(product: Product) {
if (coin <= 0) println("코인을 넣어주세요.")
else println("${product.name}이(가) 카트에 추가됐습니다.")
// 다중 상속 함수 사용
super<Order>.add(product)
}
}
다중 상속한 인터페이스에서 같은 함수 명이 있으면 super 키워드를 사용해서 해당 함수를 실행한다.
인스턴스 생성 및 사용
fun main() { val cart = MyCart(coin = 100) cart.rent() cart.roll() cart.add(Product(name = "장난감", price = 1000)) }
- 실행
enum class
키워드 사용enum class PaymentStatus(val label: String) {
UNPAID("미지급"),
PAID("지급 완료"),
FAILED("지급 실패"),
REFUNDED("환불"); // 함수를 정의하기 위해서는 세미콜론이 필요
}
enum class PaymentStatus(val label: String) {
UNPAID("미지급") {
override fun isPayable(): Boolean = true
},
PAID("지급 완료") {
override fun isPayable(): Boolean = false
},
FAILED("지급 실패") {
override fun isPayable(): Boolean = false
},
REFUNDED("환불") {
override fun isPayable(): Boolean = false
};
abstract fun isPayable(): Boolean
}
enum class PaymentStatus(val label: String): Payable {
UNPAID("미지급") {
override fun isPayable(): Boolean = true
},
PAID("지급 완료") {
override fun isPayable(): Boolean = false
},
FAILED("지급 실패") {
override fun isPayable(): Boolean = false
},
REFUNDED("환불") {
override fun isPayable(): Boolean = false
};
}
interface Payable {
fun isPayable(): Boolean
}
fun main() {
println(PaymentStatus.REFUNDED.label)
if (PaymentStatus.UNPAID.isPayable()) {
println("결제 가능 상태")
}
val paymentStatus = PaymentStatus.valueOf("PAID")
println(paymentStatus.label)
if (paymentStatus == PaymentStatus.PAID) {
println("결제 완료 상태")
}
for (status in PaymentStatus.values()) {
println("[${status.name}](${status.label}) : ${status.ordinal}")
}
}