[Kotlin] 코틀린 총 정리2 - 기본문법

김민주·2022년 10월 11일
0

Kotlin

목록 보기
2/3



코틀린 총 정리 2편 - 기본문법



Code with Joyce 님의 영상,
부스트코스 코틀린 강좌,
오준석의 안드로이드 생존코딩 책(2판)을 기반으로 작성하였습니다

1. Lamda


람다식은 우리가 value 처럼 다룰 수 있는 익명함수이다.

  1. 메소드의 파라미터로 넘겨줄 수 있음.
  2. Return 값으로 사용할 수 있음.

  • 람다의 기본정의

    val 람다이름 : Type = {argumentList -> codBody}

val square: (Int) -> (Int) = { number -> number * number }
val square = { number: Int -> number * number }

앞이든 뒤든 타입을 한 곳은 명시해줘야함 (그래야 타입 추론 가능)

val nameAge : (String,Int) ->  String = { name:String, age:Int ->
"my name is ${name} im ${age}"}

람다식의 Type은 순서대로 써줘야 함!

매개변수는 여러개일 수 있으므로 괄호를 써주자!

↪️ 만약 람다식에서만 RETURN 을 하고 싶다면?!
label을 이용한다



📍 SAM 변환

: Single Abstract Method 변환으로, 메서드가 하나인 인터페이스를 구현할 때 함수를 작성함.


  • 일반적인 버튼 클릭 이벤트 리스너 구현 코드
button.setOnClickListener(object:View.OnClickListener{
   override fun onClick(v: View?){
		//TODO
   }
})
  1. 이 코드를 람다식으로 변경
  2. 람다식을 괄호 밖으로 빼내고, 람다가 메소드의 유일한 인수이면 메소드 괄호 생략
  3. 자료형 추론 가능경우 자료형 생략
  4. 람다식에서 인수가 하나인 경우 아예 생략하고, it으로 접근가능

  • SAM변환 버튼 클릭 이벤트 리스너 구현 코드
button.setOnClickListener{
	it.visibility = View.GONE 
}

it는 View? 타입의 v인수를 가리킴.


SAM변환은 자바에서 작성한 인터페이스일 때만 동작!!

-> 코틀린에서는 인터페이스 대신 함수를 사용하는 것이 바람직.



↪️ 람다의 리턴

val calculateGrade: (Int) -> String = {
    when (it) {
        in 0..50 -> "fail"
        in 51..80 -> "pass"
        in 81..100 -> "perfect"
        else -> "Error"
    }
}

람다는 마지막 표현식이 리턴

100까지만 범위를 나누면 범위이외의 값에서는 return을 못하므로
else 절을 꼭 적어줘야 한다



🧐 람다를 표현하는 여러가지 방법

fun invokeLamda(lamda:(Double)->Boolean):Boolean{
    return lamda(5.2343)
}

Double형을 받아 Boolean을 리턴하는 람다식을 리턴하는 함수이다.

함수를 main 함수에서 호출하면

fun main(){
    val lamda={number : Double -> number == 4.3212}
    println(invokeLamda(lamda)) // false 출력
    println(invokeLamda { true }) // true 출력
    println(invokeLamda { it>3.22 }) // 넣는파라미터 1개가 3.22보다 크면 true
}
  • function의 마지막 파라미터가 람다인 경우
    lamda의 소괄호를 제거하고 중괄호만 써줄 수 있다!



1-1. 람다 적용


  • 람다식을 적용할 수 있는 익명내부함수의 조건
    1. 코틀린 인터페이스가 아니고 자바 인터페이스여야함
    2. 그 인터페이스는 하나의 메소드만 가져야 함

 button.setOnClickListener(object : View.OnClickListener{
            override fun onClick(p0: View?) {
                //TODO("Not yet implemented")
            }

        })

button.setOnClickListener {  //TODO("Not yet implemented") }

setOnClickListener는 위의 두 조건을 만족하기에 람다식 적용가능



2. Data Class


데이터클래스는 자바의 POJO 클래스를 데이터만 다루는 훨씬 간편한 문법이다.


데이터클래스 여러 개도 한 파일안에서 생성가능하며,

  • 생성자, getter와setter, toString(), hashCode(), equals(), copy(), component1() 등
    다양한 메소드를 컴파일러가 자동으로 만들어줌

  • 데이터 클래스 작성 시 기본 생성자 에 val/var로 지정한 매개변수가 1개 이상 있어야 함

  • abstract, open, sealed, inner 키워드 사용 불가함

  • 구조분해를 지원해줌 = property를 순서대로 할당해줌
    val (name,age) = Person("김민주", 24)


기존 자바에서 일일이 적어줘야했던 메소드들이 자동으로 생성되니 아주 편하다👍


data class Ticket(val companyName: String, val name: String, var date: String, var seatNumber: Int)

fun main(){
    val ticketA = Ticket("koeanAir","minju","2022-06-11", 1)
    println(ticketA) //객체 내용 출력
}

일반 클래스라면 메모리 주소값이 출력될텐데
DataClass라서 객체의 내용이 출력된다

Ticket(companyName=koeanAir, name=minju, date=2022-06-11, seatNumber=1)



3. Companion Object


Companion Object는 클래스 내부에서 선언하며, 1개만 선언할 수 있다.

동반객체는 자바의 static과 비슷한 개념이지만 static처럼 동작하는 오브젝트 객체이다!

private property나 method를 읽어올 수 있게끔 한다.


  • Companion Object 특징
    1. 인터페이스의 상속을 받을 수 있다.
    2. 1개의 인스턴스만 존재한다. (like Singleton)
    3. 이름을 붙일 수 있다.
class Book private constructor(val id:Int, val name:String){
    companion object : IdProvider {

        override fun getId(): Int {
            return 444
        }

        val myBook = "kotlin basic"
        fun create() = Book(getId(), myBook)
    }
}

interface IdProvider{
    fun getId() : Int
}
fun main(){
    val book = Book.Companion.create() // 이름이 없다면
    val book2 = Book.a.create() //companion object a { 라면
    val book3 = Book.create() //Companion 또는 이름 생략가능
    val bookId = Book.getId()
    println("${book.id} ${book.name}")
}

Output : 444 kotlin basic



4. Object Declaration


오브젝트는 컴파일될 때 한 번만 만들어지는 객체로,
Singleton Pattern 이며, Thread-safe 하다.

사용되지 않아도 생성되기 때문에 메모리에 인스턴스가 존재한다.

  • 오브젝트 특징
    1. 객체생성을 하지 않아도 바로 호출가능하다.
    2. 주 생성자, 부 생성자를 정의할 수 없다.
    3. 객체가 사용되는 시점에서 초기화가 이루어진다. (초기화 하려면 init{} 사용)
object CarFactory{
  
    val cars = mutableListOf<Car>()
    fun makeCar(horsePower: Int) : Car{
        val car =Car(horsePower)
        cars.add(car)
        return car
    }
}

data class Car(val horsePower : Int)

fun main(){
    val car = CarFactory.makeCar(10)
    val car2 = CarFactory.makeCar(200)
    println(car)
    println(car2)
    println(CarFactory.cars.size)
}



🤨 Object vs Companion object

ObjectCompanion object
선언전체가 싱글톤 객체클래스 내 일부분이 싱글톤 객체
초기화사용될 때속한 클래스가 메모리에 올라갈 때



5. 다양한 스코프 함수

5-1. 확장함수

: 원래 있던 클래스에 기능을 추가하는 함수.

  • 확장함수를 추가할 클래스에 .확장함수이름으로 작성
  • 확장함수 내부에서는 객체를 this로 접근가능 (=리시버 객체)

만약 숫자가 짝수인지 판별하려면,

fun Int.isEven() = this % 2 == 0

val a = 5
println(a.isEven()) //false

만약 String class 에 무언가 추가하고 싶다면,

val mara : String.() -> String = {
    this + " Maratang is the best!"
}


fun main(){
    val a = "minju said"
    println(a.mara()) // "minju said Maratang is the best!" 출력
}

람다랑 같이 쓰였다.

fun extendsString(name:String, age:Int):String{
    val introduceMyself : String.(Int) -> String = {
        "I am ${this} and ${it} years old"
    }
    return name.introduceMyself(age)
}

this는 확장함수가 불러낸 String 오브젝트이고
파라미터가 Int 하나이므로 파라미터가 하나면 it으로 지정가능



5-2. 고차함수

: 다른함수를 인수를 받거나 반환하는 함수.(higher-order fun)

fun add(a:Int, b:Int, callback:(sum: Int) -> Unit){
	callback(a+b)
}
//작성한 고차함수를 사용
add(5,3,{println(it)})
//함수는 {}로 감싸고, 내부반환값을 it으로 접근



5-3. let()

: 블록에 자기자신을 인수로 전달하고 결과를 반환하는 함수.

  • 인수로 전달된 객체는 it으로 참조
val result = str?.let{
	Integer.parseInt(it)
}

null이 아닐 때만 str을 정수로 변환하여 반환해주는 코드.
if문을 쓰는 것보다 훨씬 간결하게 가능함.



5-4. with()

: 인수를 객체로 받으면서 블록에 리시버 객체로 전달하는 함수.

  • 리시버로 전달된 객체를 this로 접근가능하며, 생략도 가능
  • ?. 불가능 하므로 null이 아닌 경우에만 사용
with(str){
	println(toUpperCase()) //this 생략
}



5-5. apply()

: 블록에 객체 자신이 리시버 객체로 전달되고 이 객체가 반환되는 함수.

  • 객체의 상태를 변화시키고 그 객체를 반환할 때 사용
val result = car?.apply{
	car.setColor(Color.red)
    car.setPrice(3000)
} // result에는 값이 변경된 car객체가 저장됨



5-6. run()

  1. 익명함수 처럼 사용하기
    • 블록의 결과를 반환함
    • 복잡한 계산에 임시변수가 많이 필요할 때 유용하게 쓰임
val avg = run{
	val korean = 100
    val english = 80
    val math = 98
    (korean+english+math) / 3.0
}

  1. 객체에서 호출하기
    • 객체를 블록의 리시버 객체로 전달하고 블록의 결과를 반환함
    • 안전한 호출(?.)이 가능하여 with()함수 보다 유용함
str?.run{
    println(toUpperCase()) //this 생략
}



5-7. use()

: 객체를 사용한 후 자동으로 close() 메소드를 호출해주는 함수

  • Closeable 인터페이스를 구현하고 있는 객체에 접근할 수 있는 확장함수이므로 명시적으로 클로징하지 않아도 됨






profile
𝐃𝐨𝐧'𝐭 𝐛𝐞 𝐚 𝐩𝐫𝐨𝐜𝐫𝐚𝐬𝐭𝐢𝐧𝐚𝐭𝐨𝐫💫

0개의 댓글