본문 바로가기
프로그래밍/Kotlin

[Kotlin 기초 2] Objects (3)

by 채연2 2023. 2. 17.

 

2023.02.09 - [프로그래밍/Kotlin] - [Kotlin 기초 2] Objects (2)

 

Contents

     

     

    Objects

    Lists

    List는 다른 Object를 보유하는 Object인 컨테이너이다. 컨테이너는 컬렉션이라고도 하는데, 기본 컨테이너가 필요할 때 일반적으로 List를 사용한다.

     

    fun test() {
       val ints = listOf(99, 3, 5, 7, 11, 13)
       Log.e("cylog", ints.toString());          //[1]
    
       // Select each element in the List:
       var result = ""
       for (i in ints) {                         // [2]
          result += "$i "
       }
       Log.e("cylog", result.toString());
    
       // "Indexing" into the List:
       Log.e("cylog", "ints[4] : ${ints[4]}");   //[3]
    }

    [1] : List는 자신을 표시할 때 대괄호[] 사용
    [2] : for문은 List와 잘 동작하는 것을 확인. for(i in ints)는 i가 각 값을 int로 수신한다는 의미. val i를 선언하거나 유형을 제공하지 않아도 됨.
    [3] : 대괄호는 List에 대한 인덱스

     

     

    인덱싱은 0에서 시작하므로, List의 마지막 요소 이외의 인덱스를 사용하면 아래 예제와 같이 ArrayIndexOutOfBoundsException 예외가 발생하게 된다.

    fun test() {
       val ints = listOf(1, 2, 3)
       try {
          ints[3]
       } catch (e:ArrayIndexOutOfBoundsException) {
          e.printStackTrace()
       }
    }

     

    List는 아래 예제와 같이 모든 다양한 유형을 사용할 수 있다. 또, List의 대부분 멤버 함수들은 새 개체를 생성하고 기존 개체는 변경하지 않는다.

    fun test() {
       val doubles = listOf(1.1, 2.2, 3.3, 4.4)
       Log.e("cylog", "${doubles.sum()}");
    
       val strings = listOf(
          "Twas", "Brillig",
          "And", "Slithy", "Toves"
       )
       Log.e("cylog", strings.toString());
       Log.e("cylog", strings.sorted().toString());
       Log.e("cylog", strings.reversed().toString());
       Log.e("cylog", strings.first().toString());
       Log.e("cylog", strings.takeLast(2).toString());
    }

     

     

    Parameterized Types

    유형 추론은 코드를 더 깔끔하고 가독성이 좋게 만드는 경향이 있다. 하지만 때때로 Kotlin은 사용할 유형을 파악할 수 없다고 불평하고 다른 경우에는 명확성이 코드를 더 이해하기 쉽게 만든다. 

     

    Kotiln은 초기화 값을 사용하여 숫자에 Int List가 포함되어 있고 문자열에는 String List가 포함되어 있음을 추론한다.

    아래 예제에서는 numbers2 및 strings2는 List<Int> 및 List<String>을 추가하여 생성된 숫자 및 문자열의 명시적 유형이 지정되어 있다. numbers와 strings는 유형이 지정되어 있지 않다.

     

    fun test() {
       // Type is inferred:
       val numbers = listOf(1, 2, 3)
       val strings = listOf("one", "two", "three")
            
       // Exactly the same, but explicitly typed:
       val numbers2: List<Int> = listOf(1, 2, 3)
       val strings2: List<String> = listOf("one", "two", "three")
            
       Log.e("cylog", numbers.toString());
       Log.e("cylog", numbers2.toString());
       Log.e("cylog", strings.toString());
       Log.e("cylog", strings2.toString());
    }

     

    유형 매개변수는 컨테이너 이외의 구성 요소에 유용하지만, 컨테이너와 같은 객체에서 자주 볼 수 있다. 또, 반환 값에는 유형 매개변수도 있을 수 있다.

     

    아래 예제에서 inferred()에 대한 반환 유형을 유추하는 반면 explicit()는 함수 반환 유형을 지정한다. 

    // Return type is inferred:
    fun inferred(p: Char, q: Char) = listOf(p, q)
    
    // Explicit return type:
    fun explicit(p: Char, q: Char): List<Char> = listOf(p, q)
    
    fun test() {
       Log.e("cylog", "inferred: " + inferred('a', 'b').toString());
       Log.e("cylog", "explicit: " + explicit('y', 'z').toString());
    }

     

    Read-Only and Mutable Lists

    listOf()는 수정이 불가능한 읽기 전용 List를 생성한다. List를 점진적으로 생성하는 경우, 즉 요소 없이 생성하는 경우 mutableListOf()를 사용하면 수정이 가능한 MutableList가 생성된다.

     

    아래 예제에서 볼 수 있듯이 add(), addAll() 또는 단일 요소, 다른 컬렉션을 추가하는 단축키 +=를 사용하여 MutableList에 요소를 추가할 수 있다. list에는 초기 요소가 없기 때문에 mutableListOf() 호출에서 <Int> 유형을 제공하여 Kotlin에게 유형을 알려줘야 한다.

    fun test() {
       val list = mutableListOf<Int>()
    
       list.add(1)
       list.addAll(listOf(2, 3))
    
       list += 4
       list += listOf(5, 6)
    
       Log.e("cylog", list.toString());
    }

     

    MutableList는 List로 취급될 수 있으며 이러한 경우 변경이 불가능하지만 그 반대 경우인 읽기 전용 List를 MutableList로 취급할 수는 없다.

     

    getList()에서 mutableListOf()를 사용하여 List를 생성했음에도 불구하고 반환하는 동안 결과 유형은 List<Int>가 된다. original object는 여전히 MutableList 지만 List의 lens를 통해 표시된다.

    fun getList(): List<Int> {
       return mutableListOf(1, 2, 3)
    }
    
    fun test() {
       // getList() produces a read-only List:
       val list = getList()
       list += 3
            
       Log.e("cylog", list.toString());
    }

    fun test() {
       // getList() produces a read-only List:
       val list = getList()
    //    list += 3
    
       Log.e("cylog", list.toString());
    }

     

     

    List는 읽기 전용이다. 내용을 읽을 수는 있지만 쓸 수는 없다. 기본 구현이 MutableList이고 해당 구현에 대한 가변 참조를 유지하는 경우 해당 가변 참조를 통해 여전히 수정 가능하며 읽기 전용 참조는 수정된 원본을 참조한다.

     

    아래 예제에서 first는 mutableListOf(1)에 의해 생성된 가변 객체에 대한 불변 참조이다. second는 first를 참조하고, List<Int> 유형이 지정되어있으므로 읽기 전용이다. 만약 List<Int> 유형 명시가 없었으면 Kotlin은 second도 mutableList 유형으로 추론했을 것이다.

     

    first는 변경 가능한 List에 대한 참조이므로 element 2를 추가할 수 있고, second는 이러한 변경 사항을 관찰 가능하다.

    fun test() {
       val first = mutableListOf(1)
       val second: List<Int> = first
       Log.e("cylog", second.toString());
    
       first += 2
       // second sees the change:
       Log.e("cylog", second.toString());
    }

     

    Variable Argument Lists

    List에서의 listOf() 함수는 매개변수를 얼마든지 받아서 List를 생성한다.

     

    fun test() {
       Log.e("cylog", listOf(1).toString());
       Log.e("cylog", listOf("a", "b").toString());
    }

     

    vararg 키워드를 사용하면 listOf() 함수처럼 인수를 얼마든지 취하는 함수를 정의 가능하다. vararg는 가변 매개변수 List의 약자이다.

     

    함수 정의는 하나의 매개변수로만 지정 가능하고, 매개 변수 List의 모든 item을 vararg로 지정할 수 있지만 일반적으로 마지막 item에 대해 수행하는 것이 가장 간단하다.

    fun v(s: String, vararg d: Double) {}
    
    fun test() {
       v("abc", 1.0, 2.0)
       v("def", 1.0, 2.0, 3.0, 4.0)
       v("ghi", 1.0, 2.0, 3.0, 4.0, 5.0, 6.0)
    }

     

    vararg를 사용하면 임의 개수(0 포함)의 매개변수를 전달 가능하고 모든 매개변수는 지정된 유형이어야 한다. vararg 매개변수는 배열이 되는 매개변수 이름을 사용하여 access 된다.

     

    배열과 리스트는 비슷해 보이지만 다르게 구현된다. 리스트는 일반 라이브러리 클래스인 반면, 배열은 특정 low-level 지원이 있다. 배열은 다른 언어, 특히 Java와의 호환성에 대한 Kotlin 요구 사항에서 비롯된다.

    fun sum(vararg numbers: Int): Int {
       var total = 0
       for (n in numbers) {
          total += n
       }
       return total
    }
    
    fun test() {
       Log.e("cylog", sum(13, 27, 44).toString());
       Log.e("cylog", sum(1, 3, 5, 7, 9, 11).toString());
       Log.e("cylog", sum().toString());
    }

     

    간단한 시퀀스가 필요할 때 리스트를 사용하는 것이 좋다. 다른 API에 배열이 필요하거나 가변 매개변수를 처리하는 경우에만 배열을 사용하는 것이 좋다. 대부분의 경우 vararg가 배열을 생성하고 리스트인 것처럼 처리한다는 사실을 무시할 수 있다.

    fun evaluate(vararg ints: Int) =
       "Size: ${ints.size}\n" +
       "Sum: ${ints.sum()}\n" +
       "Average: ${ints.average()}"
    
    fun test() {
       Log.e("cylog", evaluate(10, -3, 8, 1, 9).toString());
    }

     

    vararg가 허용되는 모든 element의 배열을 전달할 수 있다. 배열을 생성하려면 listOf를 사용하는 것과 같은 방식으로 arrayOf를 사용한다. 배열을 항상 변경 가능하다. Array를 일련의 매개변수로 변환하려면 확산 연산자 *를 사용하면 된다.

     

     

     

    320x100

    '프로그래밍 > Kotlin' 카테고리의 다른 글

    Kotlin 배우기 (2)  (1) 2024.02.01
    Kotlin 배우기 (1)  (0) 2024.02.01
    [Kotlin 기초 2] Objects (2)  (6) 2023.02.09
    [Kotlin 기초 2] Objects (1)  (4) 2023.02.09
    [Kotlin 기초 1] 기본 구문 (3)  (4) 2023.02.07

    댓글