변수 선언
// Top-Level에 위치한 변수
var x = 6
fun main(){
x+=1
// 키워드, 변수명, 타입, 값 순서로 작성
val a : Int = 1
// 타입 생략 가능 코틀린 컴파일러가 값을 보고 타입을 추론해줌
val b = 2
// 지연 할당 가능 단 타입을 명시해 주어야 함
val c : Int
c = 3
// 컴파일 오류
val d
d = 4
// val(value) 재할당 불가능 (상수)
// var(variable) 재할당 가능 (변수)
var e : String = "Hello World"
e = "World"
// var에서 타입이 고정되면 타입은 변경이 불가능
var f = 5
f = "Hello"
}
문자열 처리
fun main() {
val version = "1.3.50"
val javaStyle = "Hello, kotlin" + version + "!"
val kotlinStyle = "Hello, Kotlin ${version}!"
println(javaStyle)
println(kotlinStyle)
val num = 10
println("val num is equal to 10:${num == 10}.")
println("""\$""")
println("\$")
println("""
|hello
|my name is kotlin.
""".trimMargin())
}
함수 선언
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){
return a + b
}
// 반환타입이 없는 함수는 Unit을 반환한다
fun printSum(a: Int, b: Int): Unit{
println("$a + $b = ${a+b}")
}
// 디폴트 파라미터
fun greeting(message: String = "안녕하세요!!"){
println(message)
}
// named argument
fun log(level: String = "INFO", message: String){
println("[$level]$message")
}
fun main(){
}
흐름제어
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/fun main() {
// if else 사용val job = "Software Developer"
if(job == "Software Developer"){
println("개발자")
}else{
println("개발자아님")
}
// 코틀린의 if-else는 표현식 값을 리턴하는 것이 가능val age : Int = 10
var str = if(age>10){
"성인"
}else{
"아이"
}
// 코틀린은 삼항 연산자가 없다. if else가 표현식이므로 불필요하다.val a = 1
val b = 2
val c = if(b>a) b else a
val day = 2
// 자바의 switch문val result = when(day){
1 -> "월요일"
2 -> "화요일"
3 -> "수요일"
4 -> "목요일"
else -> "기타"
}
println(result)
// 여러개의 조건을 콤마로 구분해 한줄에서 정의할 수 있다.
when(getNumber()){
0, 1 -> print("0 또는 1")
else -> print("0 또는 1이 아님")
}
// 범위 연산자 .. 을 사용해 for loop 돌리기 <
for(i in 0 .. 3 ){
println(i)
}
// until 을 사용해 반복한다
// 뒤에 온 숫자는 포함하지 않는다 <=
for (i in 0 until 3){
println(i)
}
// step 에 들어온 값 만큼 증가시킨다
for (i in 0 .. 6 step 2){
println(i)
}
// downTo를 사용해 반복하면서 값을 감소시킨다
for (i in 3 downTo 1){
println(i)
}
(10 downTo 1).forEach{
print("$it ")
}
println("")
// 전달받은 배열을 반복
val numbers = arrayOf(1,2,3)
for(i in numbers){
println(i)
}
// 자바의 while문과 동일
// 조건을 확인하고 참이면 코드 블록을 실행한 후 다시 조건을 확인
var x = 5
while(x>0){
println(x)
x--
}
}
타입체크
fun main() {
fun test(obj: Any){
if (obj is Int)println("obj is Int")
if (obj is String) println("obj is String")
// obj가 String 타입이 아니라면 공백으로
println("print obj as string> ${(obj as? String?:"")}")
}
test(1)
test("Strings")
}
널 안정성 (NPE)
fun main(){
// (컴파일 에러)
// val a : String = null
var b : String = "abcdef"
// (컴파일 에러)
// b = null
// Nullable 한 타입임을 명시
var c : String? = null
// 해당 값이 null이 아니라면 실행
println(c?.length)
val d: Int = if(c!=null) c.length else 0
println(d)
// 엘비스 연산자
// ?:의 왼쪽 객체가 non-null이면 그 객체의 값이 리턴되고, null이라면 ?:의 오른쪽 값을 리턴합니다.
val e = c?.length?:0 // 해당 타입은 null 이기 때문에 0 리턴
println(e)
val f: String? = null
// !! null이 발생하지 않는다고 컴파일러에게 알리는 단언자
// null을 없애주지는 않는다.
val g = f!!.length
}
예외처리
import java.lang.Exception
import java.lang.IllegalArgumentException
fun main(){
// 코틀린은 checkedException을 강제하지 않음
Thread.sleep(1)
// 그래도 try - catch가 필요한 경우 사용가능
try{
Thread.sleep(1)
throw Exception()
}catch (e: Exception){
println("경고")
} finally {
println("finally 실행!")
}
// try - catch 문 또한 값을 반환하는 것 이 가능
val a = try{
"1234".toInt()
}catch (e: Exception){
println("예외 발생")
}
println(a)
//throw Exception("예외 발생!")
val b: String? = null
// Nothing 타입과 ?: 엘비스 연산자를 같이 사용하는 경우는 null이 나올 수 없음
val c: String = b?:failFast("예외 발생!")
println(c.length)
println("앞서 Nothing 타입이 반환된다면 이후 함수는 작동을 보장할 수 없음")
}
// throw를 return 하게 된다면 Nothing 타입을 자동으로 반환 함
fun failFast(message: String): Nothing{
throw IllegalArgumentException(message)
}
Class
// 특정 애너테이션과 같이 쓸 경우 해당 키워드 사용 권장
class Drink constructor(val name:String){
}
// 기본 생성자에 constructor 키워드 생략가능
class Coffee (
// 코드 리뷰시 가독성을 위해 후행 ,를 항상 붙일 것을 권장
var name:String = "",
var price: Int = 0,
){
val brand: String
// 커스텀 getter
get() = "스타벅스"
val origin: String
// 커스텀 getter2
get() {
return "콩고"
}
var quantity : Int = 0
// 커스텀 setter
set(value){
if(value > 0){ // 수량이 0 이상일 경우에만
// quantity = value 로 직접 할당하지 않는 이유
// quantity가 다시 set을 호출하는 무한 재귀 현상이 일어나기 때문
field = value
}
}
}
class EmptyClass
fun main(){
// getter setter 코드를 따로 작성해주지 않아도 컴파일러가 자동으로 생성 후 실행
val coffee = Coffee()
// 보이지 않는 setter 함수 실행
coffee.name = "아이스 아메리카노"
coffee.price = 2000
coffee.quantity = 5
// 보이지 않는 getter 함수 실행
println("${coffee.brand} ${coffee.name} 가격은 ${coffee.price}")
}
상속
- Kotlin의 모든 클래스의 조상은 Any class
open class Dog{
open var age: Int = 0
open fun bark(){
println("walwal")
}
}
// 코틀린에서 기본생성자 사용시 프로퍼티를 좀 더 쉽게 재정의 가능
// 상속을 받아 구현한 클래스는 프로퍼티나 함수가 자동으로 open 상태가 됨
// 이를 막고자 한다면 final 키워드를 사용해야 함
open class Bulldog(final override var age : Int = 0) : Dog(){
override fun bark(){
// 상위 클래스의 함수 호출시
super.bark()
println("kungkung")
}
}
class ChildBullDog():Bulldog(){
//override var age: Int = 0
override fun bark(){
super.bark()
}
}
// 추상 클래스
abstract class Developer{
abstract var age: Int
abstract fun code(language: String)
}
// 추상 클래스 상속시 하위 클래스에서 abstract 키워드가 붙은 것을 구현해야 함
class BackendDeveloper(override var age: Int):Developer(){
override fun code(language: String){
println("I code with $language")
}
}
fun main(){
val dog = Bulldog(age = 2)
println(dog.age)
dog.bark()
val backendDeveloper = BackendDeveloper(age=28)
println(backendDeveloper.age)
backendDeveloper.code("Kotlin")
}
인터페이스
- 하나 이상의 인터페이스 구현이 가능
class Product(val name:String, val price:Int)
interface Wheel{
// 추상 함수
fun roll()
}
// 코틀린에서는 인터페이스 에서도 프로퍼티가 존재 가능
interface Cart:Wheel{
var coin: Int
// 인터페이스 경우에는 클래스와 다르게 backing field를 사용하면 컴파일 오류 발생
val weight : String
get() = "20KG"
fun add(product: Product)
// 디폴트 함수
fun rent(){
if(coin>0){
println("카트를 대여합니다")
}
}
override fun roll() {
println("카트가 굴러갑니다")
}
fun printId() = println("1234")
}
// 복수개의 인터페이스에서 같은 이름의 함수가 있는 경우 호출이 안되는 문제점 존재
interface Order{
fun add(product: Product){
println("${product.name} 주문이 완료되었습니다")
}
fun printId() = println("5678")
}
// 상속과 달리 : 생성자가 아니라 인터페이스 명을 적어준다.
class MyCart(override var coin: Int): Cart,Order{
override fun add(product: Product) {
if(coin<=0) println("코인을 넣어주세요")
else println("${product.name}이(가) 카트에 추가됐습니다")
// 주문하기 super<인터페이스>를 통해 접근 하는 것으로 해결 가능
super<Order>.add(product)
}
// 두 개의 인터페이스에서 동일한 시그니처의 디폴트 함수를 제공한 상황에서는
// 각각을 override 해서 재정의 할 필요가 있다
override fun printId() {
super<Cart>.printId()
super<Order>.printId()
}
}
fun main(){
val cart = MyCart(coin = 100)
cart.rent()
cart.roll()
cart.printId()
cart.add(Product(name="장난감",price = 1000))
}
열거형
- 서로 연관된 상수들의 집합을 enum 클래스를 사용하여 표현
- 동등성 비교에 자주 이용
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
};
// 내부에서 추상 메서드 선언 후 구현 하는 것도 가능
// abstract fun isPayable(): Boolean
}
// 분리 작업
interface Payable{
fun isPayable(): Boolean
}
fun main(){
if(PaymentStatus.UNPAID.isPayable()){
println("결제 가능 상태")
}
// 인자로 들어온 String 을 가지고 일치하는 값을 가지고와 인스턴스 화
val paymentStatus = PaymentStatus.valueOf("PAID")
println(paymentStatus.label)
// 동등성 비교
if(paymentStatus==PaymentStatus.PAID){
println("결제 완료 상태")
}
for(status in PaymentStatus.values()){
println("[$status](${status.label})")
}
// String, 쌍, 순서
for(status in PaymentStatus.values()){
println("[${status.name}](${status.label}) : ${status.ordinal}")
}
}
'Development > Kotlin&Android' 카테고리의 다른 글
Kotlin 문법 Part 4 (0) | 2023.03.23 |
---|---|
Kotlin 문법 Part 3 (0) | 2023.03.16 |
Kotlin 문법 Part 2 (0) | 2023.03.02 |
스프링의 코틀린 지원 (0) | 2023.02.27 |
Java와 Kotlin의 차이 (0) | 2023.02.27 |