Posts [Kotlin] 코틀린 타입 시스템
Post
Cancel

[Kotlin] 코틀린 타입 시스템

Kotlin IN ACTION(출판사: 에이콘) 책을 통해 Kotlin을 배워보자

Chapter6. 코틀린 타입 시스템

1. 널 가능성(Nullability)

  • 물음표 기호 ‘ ? ‘를 사용하여 Null이 될 수 있는 여부를 컴파일러가 미리 감지하게 함.

  • NPE처리를 위해 Nullable타입을 명시적으로 지원

    • 1
      
      public void strLen(@NotNull String s1, @Nullable String s2) {...}
      
    • 1
      
      fun strLen(s1 : String, s2 : String?) {...}
      

Ex)

1
2
3
4
5
6
7
8
9
10
11
12
fun strLen(s : String) = s.length
fun strLenSafe(s : String?) = s!!.length	//모순...
fun strLenSafe2(s : String?) = if(s != null) s.length else 0

fun main(args: Array<String>) {
    println(strLen("abc"))			// 3
    println(strLenSafe("abc"))		// 3
    println(strLenSafe2("abc"))		// 3
    
    val x: String? = null
    println(strLenSafe(x))			// ? 
}

Kotlin에서는 NPE가 일어나지 않는가?

  • NullPointerException을 상속한 KotlinNullPointerException이 발생
  • 인자로 넘겨준 값이 Nullable이라면 함수내에서도 Nullable 통일!
1
2
3
4
5
public open class KotlinNullPointerException : NullPointerException {
    constructor()

    constructor(message: String?) : super(message)
}

1.1 Safe Call 연산자

  • Safe Call 연산자는 ‘ .? ‘ 을 붙여 사용
  • Null검사와 메소드 호출을 한 번의 연산으로 수행

Ex.1) Safe Call 연쇄시키기1

1
2
3
4
5
6
7
8
9
fun printAllCaps(s: String?) {
    val allCaps: String? = s?.toUpperCase()
    println(allCaps)
}

fun main(args: Array<String>) {
    printAllCaps("abc")			//ABC
    printAllCaps(null)			//null
}

Ex.2) Safe Call 연쇄시키기2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Address(val streetAddress: String, val zipCode: Int,
              val city: String, val country: String)

class Company(val name: String, val address: Address?)

class Person(val name: String, val company: Company?)

fun Person.countryName(): String {
   val country = this.company?.address?.country
   return if (country != null) country else "Unknown"
}

fun main(args: Array<String>) {
    val person = Person(name = "Dmitry", company = null)
    println(person.countryName())
}

생각해보자!

위의 Safe Call 연산자를 보고 처음엔 읽기 불편했지만 아래의 예시를 보고 느낌이 왔다.

1
2
val country = this.company?.address?.country
return if (country != null) country else "Unknown"

저 부분만 Java로 변환하면

1
2
3
4
5
6
7
8
9
if(this.getCompany() != null 
&& this.getCompany().getAddress() != null 
&& this.getCompany().getAddress().getCountry() != null)
{
 String country = this.getCompany().getAddress().getCountry();
 return country;
}else{
 return "Unknown";
}

1.2 Elvis 연산자

  • 널복합 연산자(Null coalescing) 라고도 함
  • 이항연산자로 좌항을 계산한 값이 Null인 경우, 특정 값(우항)으로 값을 할당
  • 예외 처리에 유용
  • 삼항 연산자 + Null 처리(Kotlin에서는 삼항 연산자를 지원하지 않음)

Ex .1) Safe Call 연산자 vs Elvis 연산자

1
2
3
4
5
6
7
8
//Safe Call 연산자
fun Person.countryName(): String {
   val country = this.company?.address?.country
   return if (country != null) country else "Unknown"
}

//Elvis 연산자
fun Person.countryName() = this.company?.address?.country ?: "Unknown"

Ex.2) Elvis 연산자를 이용한 예외 처리

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Address(val streetAddress: String, val zipCode: Int,
              val city: String, val country: String)

class Company(val name: String, val address: Address?)

class Person(val name: String, val company: Company?)

fun printShippingLabel(person: Person) {
    val address = person.company?.address
      ?: throw IllegalArgumentException("No address")
    with (address) {
        println(streetAddress)
        println("$zipCode $city, $country")
    }
}

fun main(args: Array<String>) {
    val address = Address("Elsestr. 47", 80687, "Munich", "Germany")
    val jetbrains = Company("JetBrains", address)
    val person = Person("Dmitry", jetbrains)
    printShippingLabel(person)					//Elsestr. 47
    										  //80687 Munich, Germany
    printShippingLabel(Person("Alexey", null))	  //java.lang.IllegalArgumentException: No address
}

1.3 Type Cast 연산자 : as 와 Safe Cast :as?

  • as?는 변환 가능한 타입인지 검사 후, 아니면 Null값 반환

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    
    class Person(val firstName: String, val lastName: String) {
       override fun equals(o: Any?): Boolean {
          val otherPerson = o as? Person ?: return false
      
          return otherPerson.firstName == firstName &&
                 otherPerson.lastName == lastName
       }
      
       override fun hashCode(): Int =
          firstName.hashCode() * 37 + lastName.hashCode()
    }
      
    fun main(args: Array<String>) {
        val p1 = Person("Dmitry", "Jemerov")
        val p2 = Person("Dmitry", "Jemerov")
        println(p1 == p2)					//true
        println(p1.equals(42))				//false
          
        println("p1 HashCode : " + p1.hashCode())
        println("p1 HashCode : " + p2.hashCode())
    }
    

    심화학습.. //혼자 다 설명할 수 없음…

    위의 코드를 보면, 뜬금없이 hashCode를 재정의한 것을 볼 수 있는데

    그 이유를 간략히 설명하자면, Effective Java에서는 다음과 같이 설명한다.

    equals를 재정의한 클래스에서는 hashcode도 재정의 해야한다. 그렇지 않으면 hash를 사용하는 HashMap, HashSet과 같은 컬렉션의 원소로 사용될 때 문제가 발생할 것이다.

    따라서, eqauls를 재정의 하는 경우 다음 3가지 규약을 지켜야한다.

    • equals비교에 사용되는 정보가 변경되지 않았다면, 객체의 hashcode 메서드는 몇번을 호출해도 항상 일관된 값을 반환해야 한다. (단, Application을 다시 실행한다면 값이 달라져도 상관없다. (메모리 소가 달라지기 때문))
    • equals메서드 통해 두 개의 객체가 같다고 판단했다면, 두 객체는 똑같은 hashcode 값을 반환해야 한다.
    • equals메서드가 두 개의 객체를 다르다고 판단했다 하더라도, 두 객체의 hashcode가 서로 다른 값을 가질 필요는 없다. (Hash Collision) 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.

    참고

    1. HashCode() 재정의 이유:

      https://jaehun2841.github.io/2019/01/12/effective-java-item11/#%EC%84%9C%EB%A1%A0

    2. 37을 곱한 이유:

      https://d2.naver.com/helloworld/831311

1.4 널 아님 단언(Not-null Assertion)

  • 널 아님 단언은 ‘ !! ‘로 표현
  • 컴파일러에게 “나는 이 값이 null이 아님을 잘 알고있으며, 예외가 발생해도 감수하겠다”라는 표현

Ex.1) Swing(프레임워크)에서 널 아님 단언 사용

1
2
3
4
5
6
class CopyRowAction(val list: JList<String>): AbstractAction(){
    override fun isEnabled(): Boolean = list.selectedValue != null
    override fun actionPerformed(e : ActionEvent){
        val value = list.selectedValue!!
    }
}
  • 한 줄에 나란히 널 아님 단언이 쓰였을 경우, 어떤 식에서 발생한 예외인지 알 수 없으니 나란히 사용은 피하자

    1
    
    person.company!!.address!!.country
    

1.5 let함수

  • 널이 될 수 있는 값(Nullable)을 널이 아닌 값(NotNull)만 인자로 받을 때, 용이하게 사용
  • 여러 값에 대한 null 체크 시, let을 중첩해서 사용하면 코드가 복잡해지므로 피하는 것이 좋다.
    • (if (something != null) 사용을 권장)

Ex.1) let을 사용해 Null이 아닌 인자로 함수 호출하기

1
2
3
4
5
6
7
8
9
10
fun sendEmailTo(email: String) {
    println("Sending email to $email")
}

fun main(args: Array<String>) {
    var email: String? = "yole@example.com"
    email?.let { sendEmailTo(it) }		//Sending email to yole@example.com
    email = null
    email?.let { sendEmailTo(it) }
}

let을 호출하면 람다의 인자인 it은 널이 될 수 있는 타입으로 추론됨

1.6 lateinit 변경자

  • lateinit 변경자를 사용하면 Nullable, NotNull을 난잡하게 사용하지않고 프로퍼티를 나중에 초기화 할 수 있다.
  • 프로퍼티가 val인 경우는 적용할 수 없고, 항상 var여야만 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import org.junit.Before
import org.junit.Test
import org.junit.Assert

class MyService {
    fun performAction(): String = "foo"
}

class MyTest {
    private var myService: MyService? = null
    //lateinit 변경자 선언 시
	private lateinit myService : MyService
    @Before fun setUp() {
        myService = MyService()
    }

    @Test fun testAction() {
        Assert.assertEquals("foo", myService!!.performAction())
        //lateinit 변경자로 선언 시,
        Assert.assertEquals("foo", myService.performAction())
    }
}
  • lateinit에 대한 초기화진행 여부를 확인하기 위해서는 ex) ::myService.isInitialized 형태로 분기처리하여 확인할 수 있다

1.7 널 가능 타입의 확장

  • null이 될 수 있는 타입에 대한 확장함수를 정의하면, Safe Call을 하지않아도 된다.

Ex.1) Null이 될 수 있는 수신 객체에 대해 확장 함수 호출

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fun verifyUserInput(input: String?) {
    if (input.isNullOrBlank()) {	//<- input이 Nullable이지만 Safe Call 연산자를 쓰지않음
        println("Please fill in the required fields")
    }
}

fun main(args: Array<String>) {
    verifyUserInput(" ")		//Please fill in the required fields
    verifyUserInput(null)		//Please fill in the required fields
}

//String에서의 확장함수 정의
public inline fun CharSequence?.isNullOrBlank(): Boolean {
    contract {
        returns(false) implies (this@isNullOrBlank != null)
    }
    return this == null || this.isBlank()
}

1.8 타입 파라미터의 널 가능성

  • Kotlin에서는 함수나 클래스의 모든 타입 파라미터는 기본적으로 널이 될 수 있다.(t를 Any 타입으로 추론하기 때문)
  • 타입 파라미터는 타입 상한을 지정하지 않는다면 Null이 될 수 있다.(t를 Any?의 타입으로 추론하기 때문)

Ex.) 널이 될 수 있는 타입파라미터와 상한 지정

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//널이 될 수 있는 타입 파라미터
fun <T> printHashCode(t: T) {
    println(t?.hashCode())
}

fun main(args: Array<String>) {
    printHashCode(null)		//null
}

//상한을 지정한 타입 파라미터
fun <T : Any> printHashCode(t: T) {
    println(t.hashCode())
}

fun main(args: Array<String>) {
    printHashCode(null)
    //Type parameter bound for T in fun <T : Any> printHashCode(t: T): Unit is not satisfied
}

참고: https://stackoverflow.com/questions/54760309/why-i-cant-assign-nullable-type-to-any-in-kotlin

1.9 플랫폼 타입

  • 플랫폼 타입은 Kotlin이 널 관련 정보를 알 수 없는 Java 타입을 지칭
  • Kotlin은 보통 NotNull타입의 값에 대해 널 안정성을 검사하지만 플랫폼 타입의 값에 대해서는 경고하지않음
1
2
3
4
5
6
7
8
9
10
//Java
public class Person{
    private final String name;
    public Person(String name){
        this.name = name;
    }
    public String getName(){
        return name;
    }
}
1
2
3
4
5
6
7
8
9
10
11
//Kotlin
fun yellAtSafe(person: Person) {
    println(person.name.toUpperCase() + "!!!")
    //toUpperCase()의 수신객체의 person.name이 non-null인데 null이라 컴파일 에러 
    println((person.name ?: "Anyone").toUpperCase() + "!!!")
    //ANYONE!!!
}

fun main(args: Array<String>) {
    yellAtSafe(Person(null))
}

전부 다 널이 될 수 있는 타입으로 하면 되지않을까?

모든 자바 타입을 널이 될 수 있는 타입으로 처리하기엔 널 안정성 검사로 인해 얻는 이익보다 쓸데없는 비용이 늘어나 프로그래머에게 타입의 널 가능성의 책임을 부여함.

1.10 상속

  • 자바클래스를 코틀린에서 상속할때, 파라미터나 반환타입의 Null처리를 결정해야함
1
2
3
4
//Java
interface StringProcessor{
    void process(String value);
}
1
2
3
4
5
6
7
8
9
10
11
12
//Kotlin
class StringPrinter : StringProcessor{
    override fun process(value: String){		// notnull 선언
        println(value)		
    }
}

class NullableStringPrinter : StringProcessor{
    override fun process(value: String?){		//nullable 선언
        value?.let{println(it)}
    }
}

2. 코틀린의 원시 타입

  • Kotlin은 원시타입과 참조타입을 구분하지 않고, 항상 같은 타입을 사용
  • 대부분, Kotlin의 Int타입은 Java의 int 타입으로 컴파일 되고, Int를 타입인자로 넘기는 경우 Integer로 컴파일된다.

2.1 null이 될 수 있는 원시타입 : Int?, Boolean? 등

  • 코틀린에서 null이 될 수 있는 원시 타입을 사용하면 그 타입은 자바의 래퍼 타입으로 컴파일 된다.

Ex.)

1
2
3
4
5
6
7
8
9
10
11
12
data class Person(val name: String, val age: Int? = null) {
    fun isOlderThan(other: Person) : Boolean? {
        if (age == null || other.age == null)
            return null
        return age > other.age    
    }
}

fun main(args: Array<String>) {
    println(Person("Sam", 35).isOlderThan(Person("Amy", 42))) //false
    println(Person("Sam", 35).isOlderThan(Person("Jane")))	  //null
}
  • age는 컴파일러에 의해 널 안정성 검사를 마친 후 값을 비교가 허용되므로, age의 프로퍼티값은래퍼 타입으로 저장된다.

2.2 숫자 변환

  • Kotlin은 Boolean을 제외한 모든 원시 타입에 대한 변환 함수를 제공

  • Kotlin은 한 타입의 숫자를 다른 타입으로 자동변환 하지 않음.

  • 명시적으로 변환 메소드를 사용해야함

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    val i = 1
    val l = Long = i  // "Error: type mismatch" 컴파일 오류 발생
      
    val i = 1
    val l: Long = i.toLong()	// 명시적으로 변환 메소드 사용
      
    ---------------------------명시 이유---------------------------
    val x = 1  // Int
    val list = listOf(1L, 2L, 3L) // Long 리스트 
    x in list // 묵시적 타입 변환으로 인해 false
      
    val x = 1
    println(x.toLong() in listOf(1L, 2L, 3L)) //true
      
    fun foo(l: Long) = pinrtln(l)
      
    val b: Byte = 1 // 상수 값는 적절한 타입으로 해석 된다.
    val l = b + 1L  // +는 Byte와 Long을 인자로 받을 수 있다.
    foo(42)         // 함수 인자이므로 42를 Long으로 해석한다.
    

2.3 Any, Any? : 최상위 타입

  • Java는 Object, Kotlin은 Any가 널이 될 수 없는 타입의 최상위타입
  • 내부적으로 Any는 자바의 java.lang.Object로 컴파일 된다.
  • 모든 코틀린 클래스에는 toString, equals, hashCode 라는 3개의 메소드가 있다. 하지만 java.lang.Object에 있는 다른 메소드(wait나 notify 등)는 Any에서 사용할 수 없다. 그런 메소드를 사용하고 싶다면 java.lang.Object로 캐스트해야 한다.

2.4 Unit 타입 : Kotlin의 void

  • Kotlin의 Unit타입은 Java의 void와 같은 기능을 함.
1
2
fun f(): Unit{...}
fun f(){...}		//fun의 기본 선언은 void라고 생각

Ex.) void 와Unit의 차이

  • 반환 타입으로 Unit을 반환가능
  • 타입 매개변수로 Unit을 쓸 수 있다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface Processor<T> {
    fun process(): T
}

//반환 타입으로 Unit을 묵시적으로 반환 가능
class NoResultProcessor : Processor<Unit> {
    override fun process() {
        // 업무 처리 코드
        
    }
}

class ResultProcessor : Processor<Integer> {
    override fun process(): Int {
        // 업무 처리 코드
        return someInt
    }
}

Kotlin은 왜 void가 아닌 Unit을 만들었을까?

Kotlin의 Unit은 Java의 void와 다르게 두 가지 특징을 가진다

  1. Unit은 싱글톤 인스턴스이다. 그래서 Kotlin에서 Unit이라는 키워드는 타입이면서도 동시에 객체

    1
    
    val unit : Unit = Unit
    
  2. Unit은 객체이기도 하기때문에 Any를 상속하는 서브 클래스이다.

    1
    
    val unit : Any = Unit
    

2.5 Nothing 타입: 이 함수는 결코 정상적으로 끝나지 않는다.

  • 함수가 정상적으로 끝나지 않는 사실을 표현할 때 사용
  • Nothing 타입은 아무 값도 포함하지 않음 = return이라는 행위 자체를 하지않음

Ex. 1) 함수가 리턴 될 일이 없을 경우

1
2
3
4
5
fun infiniteLoop(): Nothing {
    while (true) {
        println("Hi there!")
    }
}

Ex. 2) 예외를 던지는(throw Exception)함수의 리턴 타입

1
2
3
fun throwException(): Nothing {
    throw IllegalStateException()
}

Ex. 3) 함수의 리턴 타입이 Nothing? 일 경우

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fun mayThrowAnException(throwException: Boolean): Nothing? {
    return if (throwException) {
        throw IllegalStateException()
    } else {
        println("Exception not thrown :)")
        null
    }
}

fun main() {
    val result = mayThrowAnException(true)
    if (result == null) { // Always true
        println("Ignored code")
    }
}

3. 컬렉션과 배열

3.1 널 가능성과 컬렉션

Ex.1)null이 될 수 있는 값으로 이뤄진 컬렉션

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
fun readNumbers(reader: BufferedReader): List<Int?> {
    val result = ArrayList<Int?>()
    for (line in reader.lineSequence()) {
        try {
            val number = line.toInt()
            result.add(number)
        }
        catch(e: NumberFormatException) {
            result.add(null)
        }
    }
    return result
}

fun addValidNumbers(numbers: List<Int?>) {
    var sumOfValidNumbers = 0
    var invalidNumbers = 0
    for (number in numbers) {
        if (number != null) {
            sumOfValidNumbers += number
        } else {
            invalidNumbers++
        }
    }
    println("Sum of valid numbers: $sumOfValidNumbers")
    println("Invalid numbers: $invalidNumbers")
}

fun main(args: Array<String>) {
    val reader = BufferedReader(StringReader("1\nabc\n42"))
    val numbers = readNumbers(reader)
    addValidNumbers(numbers)		//Sum of valid numbers: 43
								  //Invalid numbers: 1
    
}
  • List<Int?> 과 List?과 List<Int?>? 잘 구분해서 쓸 것!

3.2 읽기 전용과 변경 가능한 컬렉션

  • Kotlin 컬렉션은 자바와 다르게 읽기 전용 컬렉션(Collection)과 변경가능 컬렉션(MutableCollection)이 분리되어있다.

    Ex) Collection(size, iterator(), contains()) / MutableCollection(add(), remove(), clear())

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    fun <T> copyElements(source: Collection<T>,
                         target: MutableCollection<T>) {
        for (item in source) {
            target.add(item)
        }
    }
      
    fun main(args: Array<String>) {
        val source: Collection<Int> = arrayListOf(3, 5, 7)
        val target: MutableCollection<Int> = arrayListOf(1)
        copyElements(source, target)
        println(target)		//[1, 3, 5, 7]
    }
    

    Ex) Thread-safe하지않은 읽기전용 Collection

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    //MutableCollection이 Collection 참조
    val collection: Collection<Int> = arrayListOf(1, 2, 3)
    val mutableCollection: MutableCollection<Int> = collection
    error: type mismatch: inferred type is Collection<Int> but MutableCollection<Int> was expected
      
      
    // Collection이 MutableCollection 참조
    val mutableCollection: MutableCollection<Int> = arrayListOf(1, 2, 3)
    val collection: Collection<Int> = mutableCollection //가능 
    >>> collection
    [1, 2, 3]
    >>> mutableCollection
    [1, 2, 3]
    >>> mutableCollection.add(5)
    >>> mutableCollection
    [1, 2, 3, 5]
    >>> collection
    [1, 2, 3, 5]    // mutalbeCollection에 5를 추가했지만 collection에도 영향을 미친다
    			   // 같은 객체를 다른 타입의 참조들이 가리키고 있다.
      			   
    
    • 따라서, ConcurrentModificationException등의 오류가 발생할 수 있으므로, 다중 스레드 환경에서 데이터를 다루는 경우 그 데이터를 적절히 동기화 하거나 동시 접근을 허용하는 데이터 구조를 활용해야한다.

3.3 코틀린 컬렉션과 자바

  • 코틀린에서 읽기 전용인 Collection으로 선언된 객체라도 자바코드에서는, 이를 구분하지 않으므로 수정가능
  • 코틀린 컴파일러가 컬렉션이 어떤 일을 하는지 정확한 분석이 어려움
  • 어차피 ImmutableList가 나와도 자바로 가면 변경이 가능하니 차라리 변경 가능한 컬렉션을 넘기고 명시를 정확히 해두는 것이 좋다.
1
2
3
4
5
6
7
8
9
10
//Java
public class CollectionUtils {
    public static List<String> uppercaseAll(List<String> items) {
        for (int i = 0; i < items.size(); i++) {
            items.set(i, items.get(i).toUpperCase());
        }
        
        return items;
    }
}
1
2
3
4
5
6
7
8
9
10
11
//Kotlin
fun printInUppercase(list: List<String>) {
    println(CollectionUtils.uppercaseAll(list))
    println(list.first())
}

fun main(args: Array<String>) {
    val list = listOf("a", "b", "c")
    printInUppercase(list)			//[A, B, C]
    							   //A
}

3.4 자바의 콜렉션 타입을 플랫폼 타입으로 다루기

  • 자바의 콜렉션 타입은 읽기전용이나 변경 가능으로 다룰 수 있음
  • 시그니처에서 콜렉션 타입을 사용한 자바 메서드를 오버라이드할 경우 코틀린 타입 선택 필요
    • 선택 사항
      • 컬렉션이 널이 될 수 있는가?
      • 컬렉션의 원소가 널이 될 수 있는가?
      • 오버라이드하는 메서드가 컬렉션을 변경할 수 있는가?
    • 자바 인터페이스나 클래스가 어떤 맥락에서 사용되는지 정확히 알아야한다.

3.5 객체의 배열과 원시타입의 배열

  • 코틀린에서 배열을 만드는 방법

    • arrayOf : 함수에 원소를 넘김

      1
      2
      3
      4
      
      fun main(args: Array<String>) {
          val strings = listOf("a", "b", "c")
          println("%s/%s/%s".format(*strings.toTypedArray()))		// a/b/c
      }
      
    • arrayOfNuls : 원소타입이 널이 될 수 있는 타입인 경우에만 호출할 수 있고, 인자에 배열의 크기를 넘김

      1
      
      val nullStorableArray = arrayOfNulls<String>(3)
      
    • Array() : 배열의 크기와 람다를 인자로 받아, 람다를 호출하여 각 배열 원소를 초기화

      1
      2
      3
      4
      
      fun main(args: Array<String>) {
          val squares = IntArray(5) { i -> (i+1) * (i+1) }
          println(squares.joinToString())		//1, 4, 9, 16, 25
      }
      
출처
This post is licensed under CC BY 4.0 by the author.