Android 앱개발 공부/TIL(Today I Learned)

[Android] TIL 14일차

bunny code 2024. 6. 10. 20:18

접근 제한자


 

접근 제한자란? : 변수나 메소드의 접근을 제한

접근이란? : 객체를 이용해서 변수나 메소드를 호출할 수 있는지의 여부

 

프로젝트: 최상단 개념이고 모듈, 패키지, 클래스를 포함

모듈 : 프로젝트 아래의 개념이고 패키지, 클래스를 포함

패키지 : 모듈 아래의 개념이고 클래스를 포함

 

public: 명시하지 않으면 기본적으로 public(어디서나 접근 가능)

private: 동일한 클래스 내부에서만 접근 가능

internal: 같은 모듈 내부에서만 접근 가능

protected: 기본적으로 private이지만 상속을 받은 경우에 모듈에서 접근 가능

 

 

 

예외 처리의 활용


* 예외 처리의 활용 : 프로그램 실행도중에 발생하는 예외를 적절하게 처리하기 위함

  • 프로그램을 실행하기 전에 알 수 있는 컴파일 에러를 오류라 함
  • 프로그램을 실행하는 도중에 발생하는 런타임 에러를 예외라 함
  • 실행 도중 예외가 발생하면 프로그램이 비정상적으로 종료

예외 처리 사용 이유? : 안정성을 높이기 위해

 

* try-catch 구조 및 예제

fun method1() {
		try {
			예외가 발생할 가능성이 존재하는 코드
		} catch(예외종류) {
			예외가 발생했을때 처리할 코드
		}
}

//숫자를 입력해야 하는데 문자를 입력했을 경우 예시(try-catch)
	while(true) {
        try {
            var num1 = readLine()!!.toInt()
            println("내가 입력한 숫자는 ${num1}입니다")
            break
        } catch(e:java.lang.NumberFormatException) {
            println("숫자를 입력하세요")
        }
    }
    
//예시2(try-catch-finally)
	while(true) {
        try {
            var num1 = readLine()!!.toInt()
            println("내가 입력한 숫자는 ${num1}입니다")
            break
        } catch(e:java.lang.NumberFormatException) {
            println("숫자를 입력하세요")
        } finally {
            println("키보드와의 연결은 정상적입니다")
        }
    }

-> finally는 try-catch와 상관없이 항상 실행되는 구문

-> catch 안에 들어가는 예외 종류는 오류 명칭을 작성

 

 

* throw 구조

fun method1(num1:Int) {
		if(num1 > 10) {
			throw 예외종류
		}
}

 

* 결론

-> 예외가 발생했을 때 안정적으로 로그 또는 기록을 남기려면 try-catch

-> 그냥 오류를 넘기기만 할 경우는 throw

 

 

지연 초기화


 

* 지연 초기화란?

  • 클래스를 설계할 때 안정성을 위해 반드시 변수의 값을 초기화할 것을 권장
  • 클래스를 설계할 때 초기의 값을 정의하기 난처해서 나중에 대입하기 위한 문법
  • 저사양일 경우 더 효율적으로 사용하기 위해서 쓰임(초기에 값이 전부 입력되어 있는 경우보다 필요할 때 초기화하는 경우가 더 효율적이기 때문)
  • 변수는 lateinit으로 상수는 lazy 지연초기화

 

* 변수의 지연초기화

  • 변수를 사용하기 전에 초기화되었는지 확인해야 안정성을 높일 수 있음 -> isInitialized를 활용해서 확인 (true/false)
  • isInitialized 사용할 때는 값이 아니라 참조형태 사용해야하기 때문에 this:: 또는 :: 붙임

 

* 변수의 지연 초기화 예제(lateinit + isInitialized 활용)

fun main(){
    var s1 = Student()
    s1.name = "참새"
    s1.displayInfo()

    s1.age = 10
    s1.displayInfo()
}

class Student {
    lateinit var name:String
    var age:Int = 0

    fun displayInfo() {
				if(this::name.isInitialized) {
	        println("이름은: ${name} 입니다.")
	        println("나이는: ${age} 입니다.")
				} else {
					println("name변수를 초기화해주세요.")
				}
    }
}

 

* 출력 결과

이름은: 참새 입니다.
나이는: 0 입니다.
이름은: 참새 입니다.
나이는: 10 입니다.

-> lateinit은 변수 선언 맨 앞에 작성

-> .isInitialized(변수를 사용하기 전에 초기화되었는지 확인)를 활용하지 않고 작성할 수 있으나 안정성 및 성능이 저하됨

-> 성능 저하 이유 : 초기화 여부를 확인하지 않으면 예외가 발생, 이로 인해 try - catch 문구도 사용해야 함

-> main 메소드에 변수 name에 대한 선언이 없을 경우 else문 내용이 출력된다.

 

 

* 상수의 지연 초기화(lazy)

  • 상수를 사용하는 시점에 값을 대입하고 초기화가 수행

* 상수의 지연 초기화 예제

fun main(){
    var s1 = Student()
    s1.name = "참새"
    s1.displayInfo()

    s1.age = 10
    s1.displayInfo()
}

class Student {
    lateinit var name:String
    var age:Int = 0
    val address: String by lazy {
        println("address 초기화") //초기화가 되었을 때 실행 될 코드
        "seoul" //초기화 값
    }

    fun displayInfo() {
        println("이름은: ${name} 입니다.")
        println("나이는: ${age} 입니다.")
        println("주소는: ${address} 입니다.")
    }
}

 

* 출력 결과

이름은: 참새 입니다.
나이는: 0 입니다.
address 초기화
주소는: seoul 입니다.
이름은: 참새 입니다.
나이는: 10 입니다.
주소는: seoul 입니다.

-> lazy는 by lazy 형식으로 작성

-> 중괄호 안에 첫 줄에는 초기화되었을 때 출력할 코드, 다음 줄에는 초기화 값 입력

 

 

 

널 세이프티


 

* 널 세이프티

  • Null 예외는 프로그램의 가용성을 저하시키는 치명적인 오류임
  • 코틀린은 이 Null 예외로부터 안전한 설계를 위해 자료형에 Null 여부를 명시할 수 있음

 

* 널 세이프티 방법

-?,!!,?.,?: 를 이용하여 널 예외로부터 벗어나려고 함

- 강제로 null이 아니라고 하는!! 는 최대한 사용 지양

 

? : null도 저장할 수 있음

!!(널 단언 연산자) : null 아님을 단언

 

 

* ? 예제

fun main(){
    var s = Student()
    s.name = "참새"
    s.address = "서울"
    s.displayInfo()
}

class Student {
    lateinit var name:String
    var address:String? = null
    
    fun displayInfo() {
        println("이름은: ${name} 입니다")
        println("주소는: ${address} 입니다")
    }
}

 

* 출력 결과

이름은: 참새 입니다
주소는: 서울 입니다

-> 만약 s.address에 서울을 넣지 않을 경우 "주소는: null"입니다 가 출력 됨

 

 

* !! 예제

fun main(){
//  var data = readLine()!!.toInt()

    var inputData = readLine()!!
    var data = inputData.toInt()
    println("Null아닌 값: ${data}")
}

-> !!(널 단언 연산자)가 있어서 위에 주석처리한 readLine()!!.toInt()도 사용가능

 

 

?.(안전 호출 연산자, safe-calls)

  • ?. 키워드로 Null인지 확인하고 Null이 아닐 때만 참조하는 메소드를 실행하도록 작성할 수 있음

* ?. 예제

fun main(){
    var s = Student()
    s.name = "참새"
    s.displayAddressLength()
    
    s.address = "서울"
    s.displayInfo()
}

class Student {
    lateinit var name:String
    var address:String? = null

    fun displayInfo() {
        println("이름은: ${name} 입니다")
        println("주소는: ${address} 입니다")
    }
    
    fun displayAddressLength() {
        println("주소의 길이는: ${address?.length} 입니다")
    }
}

 

* 출력 결과

주소의 길이는: null 입니다
이름은: 참새 입니다
주소는: 서울 입니다

 

* s.displayAddressLength()와 s.displayInfo()의 위치를 바꾼 후 출력 결과

이름은: 참새 입니다
주소는: null 입니다
주소의 길이는: 2 입니다

-> 해당 예제는 주소를 저장하는 address는 초기값이 null이기 때문에 null 위협에 놓여있음

-> 그래서 Null이 아님을 보장해야 하는데 강제로 !!를 사용하는 것이 좋지 않은 상황이라 ?.(안전 호출 연산자)를 사용함

-> null이 아닐 때만 주소의 길이를 호출하여 출력합니다. 만약 null일 경우 그냥 null을 출력

 

 

 

* ?:(엘비스 연산자)

  • 키워드를 함께 사용해서 null 대신에 다른 문자열을 출력하고 싶을 엘비스 연산자 사용

* ?: 예제

fun main(){
    var s = Student()
    s.name = "참새"
    s.displayAddressLength()

    s.address = "서울"
    s.displayInfo()
}

class Student {
    lateinit var name:String
    var address:String? = null

    fun displayInfo() {
        println("이름은: ${name} 입니다")
        println("주소는: ${address} 입니다")
    }
    
    fun displayAddressLength() {
        println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")
    }
}

 

* 출력 결과

주소의 길이는: 초기화하세요 입니다
이름은: 참새 입니다
주소는: 서울 입니다

 

-> ?: 연산자와 함께 "초기화하세요" 문구를 사용하여 null 대신 "초기화하세요" 문구가 출력됨

 

 

 

배열


* 배열

  • 변수를 선언하면 코틀린은 메모리에 띄엄띄엄 랜덤으로 생성, 변수의 위치정보가 연속적이지 않기 때문에 순서가 없음
  • 이를 배열을 통해 변수에 순서를 매겨 연속적으로 활용
  • 배열은 arrayOf 메소드(키워드) 사용

* 배열 예제

package com.example.myapplication

//Arrays를 사용하기 위해서 불러와야함
import java.util.Arrays

fun main(){
// arrayOf메소드를 호출하면 배열을 리턴
// 1,2,3,4,5 각각을 저장한 변수 5개를 배열형태로 arr에 저장
var arr = arrayOf(1,2,3,4,5)

// Arrays.toString : 배열요소를 모두 출력
println(Arrays.toString(arr))

// 배열의 첫번째 요소에 저장된 값을 출력
// var num1 = 1의 num1과 arr[0]은 동일
// arr[0]은 하나의 변수로 취급할 수 있습니다
// arr은 0~4번방(인덱스)까지 접근할 수 있습니다
println(arr[0])
}

 

* 출력 결과

[1, 2, 3, 4, 5]
1

-> 인덱스(index) : 배열 내의 각 요소의 순서

 

 

* 배열 예제 2

fun main() {
    var kors = arrayOf(90, 94, 96)
    for((idx, kor) in kors.withIndex()) {
        println("${idx}번째 국어 점수는 ${kor}입니다")
    }
}

 

* 출력 결과

0번째 국어 점수는 90입니다
1번째 국어 점수는 94입니다
2번째 국어 점수는 96입니다

-> withIndex() : 위치 정보와 값을 하나로 묶어서 리턴한다.

 

 

배우면서 느낀 점


null이 들어가면 안 될 때 무조건 !!(널 단언 연산자)를 사용하는 게 좋은 줄 알았는데 아니었다.. 안전 호출 연산자도 있어서 용도에 맞게 잘 써야겠다. 그리고 코틀린에서 배열 선언과 활용법이 생각보다 간단해서 상당히 좋은 거 같다.

 

예전에 다른 프로그래밍 언어를 배우면서 배열 파트를 제일 어려워했어서 코틀린에서도 많이 어려울까 봐 걱정했는데 코드 내용이 생각보다 간결하게 문제들 몇 번 풀어보고 복습하다 보면 금방 이해가 될 듯하다.

'Android 앱개발 공부 > TIL(Today I Learned)' 카테고리의 다른 글

[Android] TIL 16일차  (2) 2024.06.12
[Android] TIL 15일차  (0) 2024.06.11
[Android] TIL 13일차  (0) 2024.06.07
[Android] TIL 12일차  (0) 2024.06.05
[Android] TIL 11일차  (2) 2024.06.04