Kotlin 문법 접근제한자, 예외처리, 지연초기화, 널 세이프티, constructor 생성자 + 프로퍼티 값 생략, Any / Any?, 예외처리 적용예시 , 배열

박미소·2023년 12월 6일
0

코틀린

목록 보기
5/44

a. 접근제한자

  1. public: 명시하지 않으면 기본적으로 public. (어디서나 접근 가능)
  2. private: 동일한 클래스 내부에서만 접근 가능.
  3. internal: 같은 모듈 내부(app)에서만 접근 가능.
  4. protected: 기본적으로 private이지만 상속을 받은경우에 타 모듈에서 접근 가능.

  1. internal, 4. protected 예시
fun main () {

    var access = AccessTestClass()
    println(access.d)                   // internal var d = 4

    var accessProt = AccessProtected()
    accessProt.protectedTest1()
}

class AccessTestClass {
    public var a:Int = 1
    var b = 2
    private var c = 3
    internal var d = 4
    protected var e = 5
 }
 
class AccessProtected : AccessTestClass() {

    fun protectedTest1() {
        println("protected e: ${e}")  // 상속받은 클래스 내부네서만 쓸 수 있움. + private 기능
    }
}

=> 4
protected e: 5


b. 예외처리


fun main() {
    var num_a = readLine()!!.toInt()
}


Exception in thread "main" java.lang.NumberFormatException: For input string: "dskfsjld"

첫줄에 원인이 된 에러가 나온다.


  • 예외처리 한 코드
fun main() {
    while(true) {
        try {
            println("숫자를 입력하세요.")
            var num_a = readLine()!!.toInt()
            println("내가 입력한 숫자는 ${num_a}입니다.")
            break
        } catch (e: java.lang.NumberFormatException) {
            println("숫자가 아닌 문자가 포함되었습니다.")
        } finally {
            println("오늘 날짜는 23년 12월 6일입니다.")
        }
    }
}



c. 지연초기화

지연초기화를 활용하면 저사양으로 제한된 환경에서 효율적으로 메모리 사용이 가능하다.


  • 변수(var)는 lateinit 이용


변수 name 값을 바로 넣기 어렵기때문에 lateinit으로 초기화.
변수 사용 전, 초기화되었는지 확인해야 안전성이 높아진다. 그래서 Isinitialized를 활용해 조건문으로 값이 초기화되었는지 알 수 있다. -> true라면 if문 실행.

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) {         // 클래스 자신의 name을 참조(;;)해서 초기화된건지 아닌건지 true/false 반환.
            println("이름은: ${name} 입니다.")
            println("나이는: ${age} 입니다.")
        } else {
            println("name변수를 초기화해주세요.")
        }
    }
}

c - 1. 더블콜론(::)

더블콜론은 코틀린에서 메소드 참조, 프로퍼티 참조, 클래스를 참조할 때 이용한다.
코틀린에서 변수나 클래스명 앞에 더블론론(::)을 명시하면, 변수나 클래스에 대한 참조를 할 수 있다. 더블콜론을 명시하면 변수가 아닌 객체(Object)로 접근할 수 있기 때문이다.



  • 상수(val)는 lazy 이용
fun main(){
    var s1 = Student()
    s1.name = "참새"
    s1.displayInfo()         // 첫번째 프린트문

    s1.age = 10
    s1.displayInfo()		// 두번째 프린트문

    s1.displayInfo()		// 세번째 프린트문
}

class Student {
    lateinit var name:String
    var age:Int = 0
    val address: String by lazy {
        println("address 초기화")
        "seoul"
        "Inchoen"
    }

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

첫번째 프린트문 println("address 초기화") => val address 프로퍼티를 초기화 할 때 실행된다.(처음 한번만)

이후 두번째, 세번째는 lazy로 나중에 넣은 "Incheon"만 address에 저장된다.
여러 개의 메소드를 호출시켜도 위에 있는 "seoul"이 아닌 맨 아래 lazy가 붙은 "Incheon"만 address에 저장된다.



d. 널 세이프티

  • !!
    null이 아님을 강제로 보장한다. 만약 int값이 아닌 문자열 인풋이 들어가면 프로그램이 바로 종료된다. 그러므로 쓰는 것을 지양해야 한다.
var name = readLine()!!.toInt()
1) var n = readLine()!!
2) var name = n.toInt()

두 단계로 나눠져 있는데 toInt()는 null값이 들어가면 에러가 나기때문에 !!붙인 것이다.



  • ? (null 저장 가능)
    var address:String? = null -> 자료형? : null을 저장할 수 있다.
    null을 저장하지 않고 사용하려면 c.지연초기화 - 변수 lateinit 활용하면 된다.

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

null인지 아닌지 확인을 한 다음 null이 아닐 때만 호출돼 실행된다. null일 때는 null이 그대로 출력된다.

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

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

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

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

    fun displayAddressLength() {
        println("주소의 길이는: ${address?.length} 입니다") 
        // 주소 값이 없어서 길이를 추출하지 못해도 오류가 나지 않고 null 출력됨.
    }
}

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


  • ?: (엘비스 연산자)

null이라는 문자열 대신 다른 문자열로 대체할 수 있다.

println("주소의 길이는: ${address?.length ?: "초기화하세요"} 입니다")
-> address가 null인지 아닌지 확인했는데 null이면(?.) null 대신에(?:) 초기화하세요로 바꾸기.

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

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

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

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

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

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



e. constructor 생성자 + 프로퍼티 값 생략

class Archer : Character {
    var name:String
    var age:Int
    var gender:String
    var money:Int
    var hp:Int                                                                   // 이전에는 초기화를 하도록 클래스 상단에 프로퍼티 값을 적었는데 ex) var hp: Int = 0

    constructor(_name:String, _age:Int, _gender:String, _money:Int, _hp:Int) {   // construcrtor = 생성자가 있다는 것은 객체가 만들어질 때 이미
                                                                                 // 프로퍼티의 깂이 없는 상황이 없이 항상 정해져 초기화 되기 때문에 생성자를 이용하면 상단에서
                                                                                 // 일일이 프로퍼티의 값을 적지 않고 생략해도 된다.
        ~~println("${name}궁수 생성") ~~                                          // -> 이 단계에서는 name값이 정해져 있지 않고 빈 값이기에 에러남.
        name = _name
        age = _age
        gender = _gender
        money = _money
        hp = _hp
        println("${name}궁수 생성")                                              // name이 초기화 되고 값이 정해지면서 사용 가능.
    }

    override fun attack() {
        println("활쏘기!")
    }

    fun windArrow() {
        println("바람의 화살!!")
    }

    fun windJump(destination:String) {
        println("${destination}까지 도약!")
    }
}


f. Any / Any?

Any 타입은 코틀린의 최상위 타입이다(Int, String등 모든 자료형, 사용자가 만든 모든 클래스는 여기서 파생된 것이다.) Any 타입 변수는 자료형이 특별히 정해지지 않은 경우에 사용한다.

Any에 ?를 붙인 Any? 는 type들을 반환 받을때 null을 허용해 반환한다는 뜻.
null일때 오류가 나지 않는다. 대신 리턴값으로 null이 들어온다.
타입 뒤에 물음표를 널어주면 null을 허용한 변수라고 선언하는 것이다.


1) null을 대입할 수 없는 변수(not null, 널 불허용)

var data1: Int = 10
data1 = null // 불가능

2) null을 대입할 수 있는 변수(nullable, 널 허용)

var data1: Int? = 10
data1 = null // 가능


g. 예외처리로 변경된 WorldMain.kt


e: Exception => 모든 종류의 오류를 포함한 오류 클래스.

안전하게 다 toString()으로 문자열로 받아 Int가 필요한 부분만(ex.money,hp) toInt()로 처리했다.

fun main() {
    val worldName = "스코월드"

    var myName = inputMyInfo("name").toString()

    var myAge = inputMyInfo("age").toString().toInt()
    print(myAge)

    var myJob = inputMyInfo("job").toString()

    var myGender = inputMyInfo("gender").toString()

    var myMoney = inputMyInfo("money").toString().toInt()

    var myHp = inputMyInfo("hp").toString().toInt()
    ...
}

inputMyInfo의 매개변수로 type을 던져서 필요한 항목으로 찾아 들어가게 했다.
전달받은 매개변수의 값이 when 조건 안 특정 값(ex. "name")에 맞아야 함수가 실행되어 입력한 값이 반환된다.

fun inputMyInfo(type:String): Any? {
    return when(type) {      
        "name" -> {
            println("이름을 입력해주세요")
            while(true) {
                try {
                    var originName = readLine()
                    if(originName?.first() != '_' && originName?.first() != '!') {
                        return originName
                    } else {
                        println("이름을 다시 입력해주세요")
                    }
                } catch(e:Exception) {
                    println("이름을 다시 입력해주세요")
                }
            }
        }
        "age" -> {
            println("나이를 입력해주세요")
            while(true) {
                try {
                    var originAge:String? = readLine()
                    return originAge?.toInt() ?: -1
                } catch(e:Exception) {
                    println("나이를 다시 입력해주세요")
                }
            }
        }
        "job" -> {
            println("직업을 입력해주세요")
            while(true) {
                try {
                    var originName = readLine()
                    if(originName?.equals("궁수") == true || originName?.equals("마법사") == true) {
                        return originName
                    } else {
                        println("직업을 다시 입력해주세요")
                    }
                } catch(e:Exception) {
                    println("직업을 다시 입력해주세요")
                }
            }
        }
        "gender" -> {
            println("성별을 입력해주세요")
            while(true) {
                try {
                    var originGender = readLine()
                    if(originGender?.equals("남") == true || originGender?.equals("여") == true) {
                        return originGender
                    } else {
                        println("성별을 다시 입력해주세요")
                    }
                } catch(e:Exception) {
                    println("성별을 다시 입력해주세요")
                }
            }
        }
        "money" -> {
            println("초기자본을 입력해주세요")
            while(true) {
                try {
                    var originMoney:String? = readLine()
                    return originMoney?.toInt() ?: -1
                } catch(e:Exception) {
                    println("초기자본을 다시 입력해주세요")
                }
            }
        }
   		...
        else -> {
        	return "no"
        }
    }
}

만약 "name" 대신 var myName = inputMyInfo("nameeeeee").toString() 으로 매개변수 type이 "nameeeeee" 로 바뀐다면, println("이름을 입력해주세요") 가 실행되지 않고 age의 <나이를 입력하세요.>출력문이 바로 실행된다.
받지 못한 프로퍼티 값은 else문의 "no"가 대신 들어가게 된다.

Any? 의 ? null 허용 덕분에 null도 리턴 받을 수 있다. ? 빼면 이 코드는 오류가 난다.

   else -> {
            return null
        }


h. 배열 - arrayOf(), withIndex()

코틀린의 내장 메소드로 배열을 만들어 사용할 수 있다. 리스트화 시켜 인덱스로 빠르케 밸류에 접근할 수 있다. 다른 패키지에 있는 java.util.Arrays 클래스의 메소드여서 반드시 import해야 한다.

  • withIndex()
    인덱스 값과 밸류 값을 하나로 묶어 리턴해준다.
fun main() {
    var arr = arrayOf(1,2,3,4,5,6,7,8,999)
    println(arr) //[Ljava.lang.String;@880ec60

    println(Arrays.toString(arr))
    println(arr[3])

    for ((idx, arr) in arr.withIndex()) {
        println("${idx}: ${arr}")
    }
}

[Ljava.lang.Integer;@880ec60
[1, 2, 3, 4, 5, 6, 7, 8, 999]
4
0: 1
1: 2
2: 3
3: 4
4: 5
5: 6
6: 7
7: 8
8: 999

arrayOf 메소드를 기반으로 객체([Ljava.lang.Integer;@880ec60)를 리턴헤서 arr에 갖고 있고 arr.withIndex 메소드를 호출하면 위치정보와 값을 하나로 묶어서 리턴해준다. for (index, arr)에서 value 자리에 arr 객체 자신이 꼭 와야 한다.

0개의 댓글