안드로이드 입문 3 일차

Ref·2024년 6월 17일

Kotlin

목록 보기
3/5

3일차

고차함수&람다

  • 고차함수(High-order function)

    • 함수를 인자로 받고 결과 값으로 내보낼 수 있다.
    • 함수 타입 표시 방법
      • (파라미터의 타입, 파라미터의 타입) -> 결과의 자료형
    fun function1(number1 : Int, number2 : Int) -> Int ={}
    fun highFunction(function: (Int, Int)->Int){
      함수내용
    }
    
    - 고차함수를 호출하는 방법
      - function1(100,100) -> 일반적인 함수
      - highFunction(function1) -> X
      - highFunction(::function1)
  • 람다(Lambda)

    • 람다함수는 그 자체로 고차함수 이기 때문에 별도의 연산자 없이 변수에 담을 수 있다.
    • 람다 안에서는 return을 쓸 수 없고, 마지막 라인이 return 된다.
    • 풀 버전 : val function3 : (String) -> Unit = {str:String -> 함수내용}
    • 파라미터가 한개인 경우라면 it을 사용한다
      • val function4 : (string) -> Unit = {println(it)}
 fun addTwoNumbers(number1:Int, number2:Int): Int {
   return number1 + number2
 }
 
 // 고차함수
 fun addTenNine(function : (Int, Int)-> Int){
 	val result:Int = function(10,9)
    println("결과는 $result")
 }
 
 // 람다
 // 풀버전 (생략이 없는)
 val addTenNine2: (Int, Int) -> Int = {number1:Int, number2:Int ->
    val result = number1 + number2
    result
}
addTenNine(addTenNine2) // 람다함수를 인자로 사용하는 경우에는 ::를 사용할 필요가 없음
// 생략버전1
val addTenNine3 : (Int, Int) -> Int = {number1, number2 ->
    number1 + number2
}
// 생략버전2
val addTenNine4 = {number1:Int, number2:Int -> 
	number1 + number2
}
// 파라미터가 없는 람다 함수
val addTenNine5 : () -> Int = {
	10 + 9
}
// 파라미터가 한개인 경우에는 it 사용
val addTenNine6 : (Int) -> Int = {
	10 + it
}

1급시민이란?

  • 변수에 담을 수 있다.
  • 함수의 인자로 전달할 수 있다.
  • 함수의 반환값으로 전달할 수 있다.

1급 객체와 1급 함수?

  • 1급 객체는 말 그대로 1급 시민의 조건을 충족하는 객체(코틀린의 함수는 객체로 사용할 수 있기 때문에 1급 객체에 속함)
  • 1급 함수는 1급 객체이면서 아래의 조건들을 추가로 만족하는 함수
    • 런타임에 생성이 가능하다
    • 익명으로 생성이 가능하다

클래스

  • 코틀린은 객체지향(Object Oriented Programing) 패러다임
  • 객체를 만드는 문법적인 요소

객체 지향

  • 객체를 통해서 문제 또는 원하는 바를 해결한다.

생성자

  • 주 생성자(Primary Constructor)
    • 클래스 이름 옆에 괄호로 둘러싸인 코드
    • 클래스를 통해서 객체를 만드는데 필요한 매개변수를 적어 준다
      • 변수명 : 변수타입
    • 반드시 한개만 존재할 수 있다
    • constructor 키워드를 생략할 수 있다
// 주생성자 -> 생략 없는 버전
class User1 constructor(name:String){
    val userName:String // class 속성(프로퍼티)
    // 속성은 init 블럭에서 초기화 됨

    init { //class가 생성될 때 호출
        println(name)
        userName = name
    }
}

// 클래스를 호출하는 법
// 클래스 호출(인스턴스화) (Instance)
// 객체 -> Object, Instance
val user = User1("심채운")

// 주생성자 -> init 생략하는 방법
class User2 constructor(name:String) {
    val userName : String = name
}

// 주생성자 -> constructor 생략
class User3(name:String){
    val userName : String = name
}

// 주생성자 -> 생략이 가능한 모든걸 생략
class User7(val name:String){

}

// 주생성자 -> 기본값 부여
class User4(name:String = "심심"){
    val userName : String = name
}
val user4 = User4()

// 생성자에서 받는 속성이 복수개인 경우
class User5 constructor(age:Int, name:String){
    val age:Int
    val name:String

    init {
        // 매개변수의 age와 프로퍼티의 age가 동일하므로 this 키워드 사용
        this.age=age
        this.name=name
    }
}
  • 부 생성자(Secondary Constructor)
    • constructor 키워드 생략 불가
    • 주 생성자에는 객체를 만들기 위한 필수 조건이 있다면, 부 생성자에는 객체를 만들기 위한 옵션 조건이 있음
    • 부 생성자에는 주 생성자가 받은 파라미터를 포함하고 있어야 한다.
    • 부 생성자는 주 생성자에게 생성을 위임해야함
    • 부 생성자는 복수개 생성할 수 있음
  • init 블록
    • 초기화 시에 필요한 작업을 하는 곳
  • 실행순서
    • 부생성자 호출 -> 부생성자 안에있는 주생성자 호출 -> init블록 호출 -> 부생성자 본문 실행
    • 클래스 속성에서 초기화를 시켜주지 않아도 되는 이유
      • 초기화 블록에서 초기화를 보장해주기 때문 -> 클래스가 생성될 때 초기화블록은 무조건 실행됨
class User8 constructor(age:Int, name:String){
    var age:Int = 0
    val name:String


    // 부생성자
    constructor(name:String, age:Int) :this(name) {
        this.age = age
    }
}

getter/setter

  • getter : 클래스의 속성에 접근 할 때
  • setter : 클래스의 속성의 값을 설정 할 때
  • 기본적으로 코틀린이 제공을 해주지만, 특별한 기능을 추가하고 싶은 경우에는 개발자가 직접 작성할 수 있음
class Book(){
    var title: String = "모름"
        get() {
            return "책 제목"
        }
        set(value) {
            println("값 :: $value")
            field = value // field가 곧 title이지만 setter에선 field라고 사용해야함
        }
//    var subTitle:String="모름"
//        get(){}
//        set(){}
}

val book = Book()
println(book.title)
book.title = "제목 변경"

late init

  • var로 선언한 프로퍼티에만 적용 가능
  • 주 생성자에선느 사용할 수 없음
  • getter/setter 적용이 불가능
  • nullable에는 적용이 불가능
  • 기초타입 프로퍼티에는 적용이 불가능
    • 원시자료형(primitive type)
  • isInitialized로 초기화 여부를 확인

더블콜론(::)

  • Reflaction 기능
  • 런타임에 프로그램의 구조를 조사할 수 있는 기능
  • lateinit의 초기화 여부는 런타임이 아니면 알 수 없다.
// lateinit
class Book(){
   lateinit var title:String

   fun nextPage(){
       if(::title.isInitialized == true){
           println("페이지 넘어감")
       } else{
           println("초기화 필요")
       }
   }
}

val book = Book()
book.title = "책이름"
println(book.nextPage())

lazy

  • 초기화를 미루고 미루고 미뤄서 정말로 필요한(접근할 때) 순간에 초기화 하겠다.
  • lazy 선언된 변수는 선언이 될 때, 혹은 init블록에서가 아니라 실제로 접근할 때 초기화가 이루어진다
  • 호출시점에 by lazy 정의에 의해서 초기화 수행한다
  • val에서만 사용이 가능하다
class Book2{
    val title : String by lazy{ // 람다
        // 본문 -> 다른 작업도 할 수 있지만 반드시 프로퍼티를 초기화 시켜주는 작업을 해야한다.
        println("lazy 초기화")
        // 만약에 엄청 복잡하고 긴 로직이 있다? 인스턴스를 생성할 때마다 로직을 돌리니깐 비효율적.
        // 정말 필요할 때만 사용하기 위해 lazy 사용
        "책 제목"
    }
}

접근제한자

  • 내가 어떻게 접근을 제한하는지에 따라 달라짐
  • public, internal, private, protected
  • 변수, 함수, 클래스 선언시 맨 앞에 적어준다
    • private val number : Int = 10
  • 패키지
    • public -> 어디에서든 접근 가능
    • internal -> 같은 모듈안에서 접근 가능
    • private -> 같은 파일안에서 접근 가능
    • protected -> 사용 안함
  • 클래스
    • public -> 클래스 외부에서 접근 가능
    • private -> 클래스 내부에서만 사용 가능
    • protected -> 클래스 자신과 상속받은 클래스에서 접근 가능
    • internal -> 사용 안함

Scope(범위)

  • 함수, 클래스, 변수
  • 규칙
    • 같은 스코프 안에서는 공유가 가능하다
    • 하위 스코프에서는 상위 스코프 맴버를 사용 및 재정의(할당) 할 수 있다.
      • 상위 스코프에서는 하위 스코프에 있는 멤버를 사용 및 재정의 할 수 없다.
  • 스코프에 따라서 접근 할 수 있는 스코프가 있고 접근할 수 없는 스코프가 있다 -> 개발자 의도와 상관 없음

상속

  • 확장된 클래스를 만들고 싶은 경우
  • 이미 존재하고 있는 클래스를 합칠 때
  • 상속은 공통점 찾기가 아님

open

  • open이라는 키워드로 설정된 클래스만 상속할 수 있다
  • open 키워드는 class 앞에 적어준다
  • final로 설정된 클래스는 상속할 수 없다. -> 기본
  • 슈퍼클래스/서브클래스, 부모클래스/자식클래스
  • 서브클래스는 슈퍼클래스의 생성을 책임져야한다
  • 슈퍼클래스가 사용하고 있는 멤버와 동일한 이름의 멤버를 만들 수 없다
  • 자식 클래스는 부모 클래스가 가지고 있는 모든 것을 물려 받는다
  • 상속은 여러번 할 수 있다
  • 다중상속은 불가능하다(A,B를 상속하는 클래스 C는 불가능)
open class Warrior1(var name:String, var power:Int, var type:String) { // 부모클래스, 슈퍼클래스
    fun attack() {
        println("공격")
    }
}

class DefenseWarrior1(name:String, power:Int):Warrior1(name, power, "고블린"){
    fun defense(){
        println("방어")
    }
}

val defenseWarrior: DefenseWarrior1 = DefenseWarrior1("똑똑한 고블린", 100)
defenseWarrior.defense()
defenseWarrior.attack()

오버라이딩(Overriding)

  • 슈퍼클래스가 사용하고 있는 맴버와 동일한 이름의 맴버를 만들 수 없다.
    • 오버라이딩을 통해서 슈퍼클래스가 가지고 있는 함수를 재정의 할 수 있다.
    • 재정의를 할 때는 함수 맨 앞에 override 키워드를 적어 준다.
  • 재정의
    • 재선언과 동일
    • 이전에 정의한 것을 번복하고 새롭게 정의하겠다
      • 번복 -> 이전에 정의한 것은 작동하지 않는다
    • 이전 것을 덮어쓴다

오버로딩(Overloading)

  • 동일한 이름의 함수를 복수개 만드는 것은 불가능하다
    • 복수개 있는 경우에 어떤걸 사용자가 사용하기를 원했는지 알 수 없기 때문
    • 어떤걸 사용자가 사용하기를 원했는지 알 수 있다면 동일한 이름의 함수를 복수개 만들 수 있다
      • 함수가 받는 인자가 다른 경우

0개의 댓글