GDSC HUFS 4기/Kotlin Team #3

[3팀] Android-12-kotlin : 접근제어자, 중첩 클래스, Safe Cast, 예외처리

홓옇 2022. 10. 8. 14:49

이 글은 Android12 및 코틀린 완전 정복를 참고하여 작성하였습니다.

작성자 : 정호영

개발환경은 Windows, Android Studio입니다.

 

1. 접근제어자 

public : 접근제어자를 표시하지 않을 시 기본 값누구나 접근이 가능하다.

private : 파일 안(해당 클래스안에서만 접근이 가능하다.

internal : 프로젝트의 모듈안에서는 누구나 접근이 가능하다.

protected : 최상위에 정의되는 클래스로 인터페이스에서는 사용할 수 없다.

 

Scope : public > protected / internal > private

 

++ 모듈이란 프로젝트 바로 아래의 개념으로 패키지와 클래스를 포함한다.

 

 

open class Outer {
    private val a = 1
    protected open val b = 2
    internal open val c = 3
    val d = 4  // public by default

    protected class Nested {
        public val e: Int = 5
    }
}

class Subclass : Outer() {
    // a is not visible
    // b, c and d are visible
    // Nested and e are visible

    override val b = 5   // 'b' is protected
    override val c = 7   // 'c' is internal
}

class Unrelated(o: Outer) {
    // o.a, o.b are not visible
    // o.c and o.d are visible (same module)
    // Outer.Nested is not visible, and Nested::e is not visible either
}

2. 중첩 및 내부 클래스

자바의 경우 A 클래스안에서 B 클래스를 정의하면 B 클래스는 자동으로 A의 내부클래스가 되었지만 코틀린에서는 아님.

A 클래스 안에 B 클래스를 정의하면 기본적으로 중첩 클래스가 되지만 B 클래스를 내부 클래스로 만들고 싶다면 inner 키워드로 클래스를 선언한다.

inner 클래스는 기본적으로 외부 클래스를 참조하여 객체에 접근하지만 중첩 클래스는 그렇지 않다.

 

class Outer {
    private val bar: Int = 1
    class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

fun main() {
    print(demo)
}

이 경우를 출력해보면 Unresolved reference: bar 에러가 발생한다.

 

중첩된 클래스에 inner 키워드를 붙여주면 

class Outer {
    private val bar: Int = 1
    inner class Inner {
        fun foo() = bar
    }
}

val demo = Outer().Inner().foo() // == 1

fun main() {
    print(demo)
}

demo 는 1이 출력된다.

 

interface OuterInterface {
    class InnerClass
    interface InnerInterface
}

class OuterClass {
    class InnerClass
    interface InnerInterface
}

인터페이스, 클래스는 여러개의 인터페이스, 클래스를 중첩클래스로 가질 수 있다.

 

3. Null Safety

첫 주 차 Nullable 에 배웠던 내용과 더해져 다양한 Null 방지 방법이 있다.

 

1. 조건에서 Null 확인

val l = if (b != null) b.length else -1

2. Safe Call 사용

 

person?.department?.head = managersPool.getManager()

처음으로 person이 널인가체크 후 department가 널인지 체크한다.

var a = "코틀린"
val b: String? = null
println(b?.length)
println(a?.length) // Unnecessary safe call

위의 경우 Null 일 경우 Null 을 출력하지만 Null 이 아닐 경우만 출력하고 싶을 수 있다. 그럴땐 let 호출 연산자를 사용한다.

 

val listWithNulls: List<String?> = listOf("Kotlin", null)
for (item in listWithNulls) {
    item?.let { println(it) } // prints Kotlin and ignores null
}

 

3. 엘비스 연산자

?: 을 기준으로 왼쪽은 널이 아닐때의 값과 오른쪽은 널일 경우 디폴트 값이다.

// if-else
val l: Int = if (b != null) b.length else -1

// Elvis Operator
val l = b?.length ?: -1

 

4. Safe Cast

Int 값을 String 으로 파싱하지 않고 강제로 Casting 해버리면 ClassCastException 이 발생한다.

이를 방지하기 위해서 as? 를 지원한다.

 

val a1 = "1"
val a1Int: Int? = a1 as? Int // a1Int = null;

val a2 = 1
val a2Int: Int? = a2 as? Int // a2Int = 1;

4. 예외 처리

1. Throw

fun main() {

   val maxAge = 150
    if(maxAge !in 0..100) {
        throw IllegalArgumentException("최대 나이는 100살입니다.")
    }
    else {
        print(maxAge)
    }

}
Exception in thread "main" java.lang.IllegalArgumentException: 최대 나이는 100살입니다.
	at com.professionalandriod.apps.kotlinbasics.BasicsKt.main(Basics.kt:8)
	at com.professionalandriod.apps.kotlinbasics.BasicsKt.main(Basics.kt)

 

자바와의 차이점은 throw가 식이므로 new 키워드를 사용하지 않아도 된다는 점이다.

 

2. 

fun main() {
    try {
        val tempNum = "100"
        val tempIntNum = tempNum as Int
        val divideNum = 100 / 0;
    } catch (e: ArithmeticException) {
        println(e)
    } catch (e : ClassCastException) {
        println(e)        
    }
    finally {
        print("여기는 항상 실행된다.")
    }
}

try 문 하나에도 여러개의 catch문이 대응될 수 있으며, finally는 오류가 발생하던 안하던 항상 실행된다.

위 경우는 터미널 출력은 아래와 같이 나왔다.

java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Integer 
(java.lang.String and java.lang.Integer are in module java.base of loader 'bootstrap')
여기는 항상 실행된다.

try를 기준으로 2번째 줄에서 String을 Int로 변환 할 수 없기에 에러가 발생하였고 catch로 잡아 내었다.

테스트 결과 에러가 발생되면 두번째 에러는 실행되지 않고 finally 코드를 출력하고 종료되는 것을 알 수 있다.