ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Kotlin] 가변인자 vararg
    Kotlin/Function 2021. 12. 25. 19:53

    가변인자 vararg 변수 선언을 통해 인자 여러개를 대입할 수 있다.

    vararg를 정확하게 이해하여 Collection api를 보다 더 쉽게 이해하도록 하자.


    가변인자를 이해하기 전에 필요한 코틀린의 배열 기초 내용을 정리하였다.

     

    • 코틀린의 IntArray, ByteArray, DoubleArray, CharArray 등의 클래스 ( primitive타입명Array 클래스)

    -> JVM의 int[], byte[], double[], char[] 등의 primitive 타입 배열로 타겟팅된다.

     

    Library.kt 파일의 top-level 함수 타입ArrayOf 메서드를 사용하여 자바의 primitive 타입 배열인 코틀린 타입Array 객체를 생성한다.

     

    • 코틀린의 Array<T> 클래스

    -> JVM의 객체 배열인 T[] 배열로 타겟팅된다.

     

    Array<String> -> String[] 

    Array<Object> -> Object[]

     

    Library.kt 파일의 top-level 함수 arrayOf 메서드를 사용하여 자바의 객체 배열인 코틀린 Array<T> 객체를 생성한다.

     

    이제 가변인자에 대해서 알아보자.


    fun main() {
    
        printElement(1, 2, 3, 4)
    }
    
    fun printElement(vararg element: Int) {
    
        println("클래스: ${element.javaClass}")
    
        for(item in element){
            print("$item, ")
        }
    }

    일반 파라메터 선언이 아닌 가변인자를 받는 파라메터 선언임을 명시하기 위해 vararg 수식어를 맨 앞에 붙인다.

    가변인자 변수 element의 Int 타입 선언은 넣어줄 여러 인자들에 해당하는 타입으로 선언할 뿐, 실제로 변수 element는 primitive int 배열이다.

     

    jvm 배열 


    2. 기존에 존재하는 배열안에 포함된 값들을 가변인자들로 전달하고 싶다면 배열을 값들로 나열시키는(펼치는) spread 연산자 * 를 사용하여 전달할 수 있다.

    spread 연산자 * 사용하지 않아 컴파일 에러

     

    fun main() {
    
        val intArray: IntArray = intArrayOf(1, 2, 3, 4)
        printElement(*intArray) //배열을 값들로 펼침
    }
    
    fun printElement(vararg element: Int) {
    
        println("클래스: ${element.javaClass}")
    
        for (item in element) {
            print("$item, ")
        }
    }

     

     

    현재 가변인자 변수 선언시 타입이 Int 로 되어있다.

    primitive 타입(현재 Int)으로 선언되어있으면 객체배열을 만들 필요가 없기 때문에 primitive 타입 배열인

    타입명Array (IntArray) 클래스가 된다.

     

    즉 primitive int 배열이므로 펼쳐서 전달할 때 객체 배열을 사용하면 안된다. *(Array<Int>객체) 를 사용하면 안된다.


    3. primitive 타입 이외의 객체 타입 선언은 객체 배열로 만들어진다. 그러므로 배열 전달시 *(객체 배열) 해야 한다.

     

    fun main() {
    
        val stringArray: Array<String> = arrayOf("1", "2", "3", "4")
        println("전달할 배열변수에 담긴 객체의 클래스: ${stringArray.javaClass}")
    
        printElement(*stringArray)
    }
    
    fun printElement(vararg element: String) {
    
        println("가변변수에 담긴 객체의 클래스: ${element.javaClass}")
    
        for (item in element) {
            print("$item, ")
        }
    }

     

     

    java 객체 배열 클래스 확인

    public class ArrayTest {
    
    	public static void main(String[] args) {
    
    		String[] stringArray = new String[5];
    		System.out.println(stringArray.getClass());
    		
    		Object[] objectArray =new Object[5];
    		System.out.println(objectArray.getClass());
    	}
    	
    }

     

    ( String을 담는 객체 배열, Object를 담는 객체 배열)


    4. 가변 파라메터의 타입선언은 지네틱 타입도 가능하다.

     

    이번 내용을 위해 2~3번의 객체 배열, primitive 배열을 따로 설명한 이유이다.

    지네릭 타입으로 선언하면 공통된 동작으로 설계하기 위해 항상 객체 배열 Array<T>로 생성된다.

    fun main() {
    
        // printElement(*intArrayOf(1)) //IntArray (primitive int type)
        // 에러 - 타입 변수 T로 인해 객체 배열만 넘길 수 있다.
        printElement(*arrayOf(1)) //Array<Int> (reference type)
        printElement(*arrayOf("1")) //Array<String>
    }
    
    fun <T> printElement(vararg element: T) {
    
       println(element.javaClass)
    }

     

    지네릭 타입 가변인자에 배열을 풀어서 넣을 때, 실수로 * 을  붙이지 않게 될 경우 우리가 기대한 동작과 다른 결과가 나올 수 있다.

    fun main() {
    
        //printElement(arrayOf("1"))
        //실수로 *를 사용하지 않은 경우
        //T = String 이 아니고 Array<String> 이 된다.
        //즉 가변인자의 각 값들이 배열이 된다.
        //element는 String 배열들을 담는 배열이 된다.
    
        printElement(arrayOf("1"), arrayOf("1", "2"))
    }
    
    
    fun <T> printElement(vararg element: T) {
    
        println(element.javaClass)
    }

    배열 arrayOf("1"), 배열 arrayOf("1","2") 자체가 가변인자의 하나가 되어버린다.

    element는 배열을 담는 배열이 되어버린다.

     

    element[0]이 참조하는 것 arrayOf("1")

    element[1]이 참조하는 것 arrayOf("1","2")가 된다.

    즉 2차원 배열이 된다. 

     

    다음의 코드와 같다.

    fun main() {
        printElement(arrayOf("1"), arrayOf("1", "2"))
    }
    
    fun printElement(vararg element: Array<String>) {
    
        println("element.javaClass: ${element.javaClass}")
        println("element.size: ${element.size}")
        println()
    
        //배열의 인덱스로 반복
        for (index in element.indices) {
            println("element[$index]에 해당하는 배열 ")
            
            //배열의 값으로 반복
            for (item in element[index]) {
                print("$item, ")
            }
            println()
        }
    }

    [[ -> 2차원 배열


    5. 가변인자에 대입된 값이 없을 경우

     

    명시적으로 타입이 선언된 경우 값을 넣지 않아도 되고, 빈 배열이 생성된다.

    fun main() {
        printElement()
    }
    
    private fun printElement(vararg element: Int) {
    
        println("element.javaClass: ${element.javaClass}")
        println("element.size: ${element.size}")
    }

     

    지네릭 타입인 경우 가변인자가 없으면 컴파일러가 어떤 객체 배열을 만들지 모르게 된다.(타입변수의 타입 유추 불가)

     

    이러한 경우 인자가 없는 메서드를 오버로딩하여 인자가 없을 때의 동작을 수행시킬 수 있다.

    가변인자의 값들을 포함하는 List<T>객체로 만들어주는 listOf 메서드는 다음과 같이 되어있다. 

     

    오버로딩 테스트

    fun main() {
        test()
        test(1)
        test("1")
    }
    
    fun test() {
        println("파라메터 없을 때의 로직 설계...")
    }
    
    fun test(vararg element: Int) {
        println("가변 파라메터 Int 타입 호출")
    }
    
    fun <T> test(vararg element: T) {
        println("가변 파라메터 T 타입 호출")
    }

    댓글

Designed by Tistory.