타입스크립트와 비슷한 면이 있다.
var x =- 5
fun main() {
x+=1;
println(x);
val a : Int = 1;
val b = 1;
val c = Int;
// c=3; 오류
val d : Int
d = 123
}
var : 가변 변수
val : final과 같은 역할
코틀린은 클래스를 만들지 않아도 변수를 선언할 수 있는데 이 것을 탑 레벨에 위치한 변수라고 한다. 또한 함수도 만들 수 있다.
// 기본 함수 선언 스타일
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) {
println("sum of $a and $b = ${a+b}}")
}
// 디폴트 파라미터
fun greeting(message: String = "안녕하세요!!") {
println(message)
}
fun main() {
greeting("Kotlin")
greeting()
}
fun log(level: String = "INFO", message: String) {
println("[$level]$message")
}
// named argument
fun main() {
log(message = "인포 로그")
log(level= "DEBUG", "워닝 로그")
}
코틀린은 타입을 지정하지 않으면 Unit이라는 타입이 default로 되어있다.
자바 void와 차이: void는 타입이 아니지만 Unit은 타입이라 제네릭/타입인수로 쓸 수 있다.
Nothing과 차이:
Unit: “정상적으로 끝났지만 줄 값이 없음.”
Nothing: “절대 정상 반환되지 않음(예: 예외 던짐, 무한루프).”
자바는 디폴트 파라미터가 없기 때문에 null에 방어적인 코드를 작성해야 된다.
fun main() {
val age : Int = 10
val str = if (age>10) {
"성인"
}else{
"아이"
}
val str2 = if(age>10) "성인" else "아이"
}
자바에서는 if-else는 구문이고 코틀린에서는 표현식이다.
fun main() {
val day = 2
val result = when(day) {
1 -> "월요일"
2 -> "화요일"
else -> "기타"
}
println(result)
when (getColor()){
Color.RED -> print("red")
Color.BLUE -> print("blue")
Color.GREEN -> print("green")
else -> print("else")
}
when(getNumber()){
1,2 -> print("1 또는 2")
else-> print("나머지")
}
}
fun getNumber() = 2
enum class Color {
RED, GREEN, BLUE
}
fun getColor() = Color.RED
... 은 범위 a<= x<= b
until은 범위 a<= x< b
step x는 x 만큼 건너서
for( i in 0...10 step 2) { println(i) }
0 2 4 6 8 10 출력됨
downTo 사용해서 감속
for (i in 3 downTo 1) {println(i)}
3 2 1 출력됨
fun main() {
val numbers = arrayOf(1,2,3)
for (i in numbers) {
println(i)
}
출력 1,2,3
}
while 문은 자바와 다를게 크게 없다.
null 을 발명한 토니호어는 1965년 null 발명후 수십년간 수십억 달러의
시스템 오류와 피해가 발생했기 때문에 1조원 짜리 실수 였다고 합니다. ?NPE를 줄이기 위해 JDK8 에서 Optional을 지원 시작
Optional은 값을 래핑하기 때문에 객체 생성에 오버헤드가 발생하고, 컴파일 단계에서 Null 가능성을 검사 X코틀린을 비롯한 최신 언어에선 널 가능성을 컴파일러가 미리 감지해서 NPE 가능성을 줄일 수 있다.
fun main() {
var a : String?= null
println(a?.length)
val b : Int = if( a!=null) a.length else 0
println(b)
// 엘비스 연산자
val c = a?.length ?: 0
println(c)
}
Null 참조에 대해서는 안전 연산자와 엘비스 연산자를 통해서 접근하게 되면 NPE없이 정상적으로 사용 가능
!! 은 개발자가 null이 없다고 확신할 때 사용하는데 단언 연산자라고 한다.
코틀린을 사용한다고 무조건 널에 안정적이는 않다.
NPE를 throw 한다거나 !! 으로 단언 연산자를 했는데 null이 들어온다면 문제가 발생한다.
코틀린은 자바와 다르게 checked exception을 강제하지 않는다.
코틀린은 개발 생산성과 API 안정성, 함수형/비동기 친화성을 위해 checked 예외를 버렸습니다.
fun main() {
var a : String?= null
println(a?.length)
val b : Int = if( a!=null) a.length else 0
println(b)
// 엘비스 연산자
val c : Int? = null
val d : Int = c ?: fail()
println(d.length)
}
fun fail() : Nothing {
throw IllegalArgumentException()
}
함수에서 throw로 에러를 반환 시 Nothing 타입이 반환된다.
엘비스 연산자와 Nothing을 같이 사용하면 null이 절대 나올 수 없다는 것을 보장함으로 null처리 없이 사용 가능하다.
class Coffee (
var name: String = "",
var price: Int = 0,
var iced: Boolean = false,
){
// 커스텀 getter
val brand: String
get() {
return "스타벅스"
}
// 커스텀 setter
var quantity : Int = 0
set(value) {
if(value>0){
field = value
}
}
}
fun main() {
val coffee = Coffee()
coffee.name = "아이스 아메리카노"
coffee.price = 2000;
coffee.iced = true;
if(coffee.iced){
println("아이스 커피")
}
println("${coffee.name} 가격은 ${coffee.price}")
}
트레일링 콤마를 지원해서 코드 리뷰시에 명확하게 변경 부분을 확인할 수 있다.
필드에 getter, setter가 포함되어 있다.
field 식별자를 사용해서 스택오버플로우를 방지한다.
코틀린에서 모든 상속의 조상은 Any다.
클래스의 기본은 final 이고 open을 통해 상속이 가능하다.
자바의 상속에 따른 부작용을 방지하기 위해 final을 사용하는데 대표적으로 System 클래스가 있다.
class Bulldog(override var age: Int = 0) : Dog() {
override fun bark() {
println("월월")
}
}
abstract class Developer {
abstract var age: Int
abstract fun code(language: String)
}
class BackendDeveloper(override var age: Int = 0) : Developer() {
override fun code(language: String) {
println("I code with $language")
}
}
fun main() {
val dog: Dog = Bulldog()
dog.bark()
println(dog.age)
val backendDeveloper: Developer = BackendDeveloper(age= 30)
backendDeveloper.code(language = "kotlin")
}
인터페이스 자체에서 백킹 필드를 사용할 수 없다.
자체적으로 클래스를 생성하는 것이 아니니까? 당연한 것 같다.
class Product(val name: String, val price: Int)
interface Cart {
var coin: Int
fun add(product: Product)
fun rent() {
if (coin > 0) {
println("카트를 대여합니다.")
}
}
fun printId() = println("6789")
}
interface Order {
fun add(product: Product) {
println("${product.name}이 주문이 완료 되었습니다.")
}
fun printId() = println("1234")
}
class MyCart(override var coin: Int) : Cart, Order {
override fun add(product: Product) {
if (coin <= 0) {
println("코인을 넣어 주세요")
} else {
println("${product.name}이(가) 카트에 추가됐습니다.")
}
// 주문하기
super<Order>.add(product)
}
override fun printId() {
super<Cart>.printId()
super<Order>.printId()
}
}
fun main() {
val cart = MyCart(10)
cart.rent()
cart.add(Product(name = "장난감", price = 2400))
cart.printId()
}