IT/Android(비공개)

4. Kotlin(코틀린)_3

상짱 2020. 3. 2. 17:16
반응형

- 람다( Lambda )

- 람다 표현식의 줄인 말

- 익명 함수를 표현하는 방법

- 익명 함수란 실행 가능한 코드 블럭

 

- 람다 표현 방법

- 먼저 중괄호를 쓰고 함수의 파라미터를 쓴 뒤 '->' 기호로 실행되는 코드 블럭을 정의

val sum = {x:Int, y:Int -> x + y}

- 최상위 프로퍼티로 바로 접근 가능

- 코틀린이 함수를 일급 객체로 취급하기 때문

 

- 일급 객체( First Class Citizen )

- 변수와 자료 구조에 할당 가능해야 한다.

- 함수의 파라미터로 전달이 가능해야 한다.

- 함수의 반환값으로 전달이 가능해야 한다.

- 동등성 비교가 가능해야 한다.

// 람다표현식 내부에서 람다표현식을 반환
val exp = { x: Int, y: Int ->
    { z: Int -> (x + y)*z }
}

// exp2 --> { z: Int -> (x + y)*z }
val exp2 = exp( 1 , 2 )

// result --> ( 1 + 2 ) * 3
val result = exp2( 3 )

 

- Collection 의 함수형 API

- filter : 컬렉션에서 조건에 맞는 항목만 추출해 컬렉션을 반환한다.

- map : 컬렉션에 항목을 변환하여 새로운 걸렉션을 만들고 반환한다.

- flatmap : 컬렉션의 포함된 항목들을 평평하게 펼친 뒤 변환하여 새로운 컬렉션을 반환한다.

- find : 함수의 조건을 만족하는 항목 한 개를 반환한다.

- group by : 컬렉션을 여러 그룹으로 이뤄진 맵으로 변경한다.

@Test
fun testCollectionApi(){
    // 컬렉션을 만든다
    val list = listOf( 1, "2", 3, 4, 5.6, 7, 8 )

    // filter : 컬렉션에서 특정 조건이 맞는 항목만 추출하여 새로운 컬렉션을 만든다. -> Int 타입만 추출한다.
    println( list.filter{ item -> item is Int })
    // [1, 3, 4, 7, 8]

    // 람다 표현식에서 파라미터가 하나인 경우 생략이 가능하다.
    // 파라미터는 it 키워드로 접근 가능
    println( list.filter{ it is Int })
    // [1, 3, 4, 7, 8]

    // map : 컬렉션에서 아이템을 변환하여 새로운 컬렉션을 만든다. 아래코드는 String 의 컬렉션이 만들어진다.
    println( list.map{ "value: ${it}" })
    // [value: 1, value: 2, value: 3, value: 4, value: 5.6, value: 7, value: 8]

    // filter 에서 반환된 컬렉션을 map 으로 변환
    println( list.filter{ it is Int }.map{ "value: ${it}" })
    // [value: 1, value: 3, value: 4, value: 7, value: 8]

    // 아이템을 찾는다.
    println( list.find{ it is Double })
    // 5.6

    // 컬렉션을 그룹화하여 Map<String, List<T>> 형태로 만든다. 아래코드는 각 아이템의 클래스 별로 그룹화 된다.
    val map = list.groupBy { it.javaClass }
    println( map )
    // {class java.lang.Integer=[1, 3, 4, 7, 8], class java.lang.String=[2], class java.lang.Double=[5.6]}

    // 컬렉션 안에 컬렉션이 있는 새로운 리스트를 만든다.
    val list2 = listOf( listOf(1,2), listOf(3,4))
    println( list2 )
    // [[1, 2], [3, 4]]

    // map 으로 항목을 변환한다.
    println( list2.map{ "value: ${it}" })
    // [value: [1, 2], value: [3, 4]]

    // flatmap 으로 리스트를 평평하게 만들고 변환한다.
    println( list2.flatMap { it.toList() })
    // [1, 2, 3, 4]

    //Assert.assertEquals(4, 2 + 2)
}

 

* 람다는 람다 표현식을 줄인 것으로 익명 함수를 표현하는 방법을 의미합니다.

* 코틀린은 중괄호 안에 파라미터와 실행 코드 블럭을 정의하여 람다식을 사용합니다.

* 코틀린의 컬렉션은 람다식을 활용하여 filter , map , flatmap , find , group by 등 유용한 함수를 제공합니다.

 

 

- 확장함수

- JAVA API 와 라이브러리 코드를 변경하지 않고도, 코틀린으로 만든 새로운 메소드나 함수를 마치 기존 JAVA API 에 원래 있었던 함수처럼 추가하여 사용 가능하다.

- 이미 정의된 클래스를 전혀 수정하지 않고도 클래스에 포함된 함수처럼 사용.

- public 으로 공개된 메소드와 멤버에 한정됨.

- Java 에는 확장함수 개념이 없기 때문에 '정적(static)함수' 일 뿐이다.

// String 클래스에 lastString 확장함수를 추가로 정의한다.
fun String.lastString(): String {
    return this.get(this.length - 1).toString()
}
// String 수신객체 타입
// this 수신객체

 

* 확장 함수는 클래스 외부에서 정의하지만, 마치 클래스의 멤버 메소드처럼 사용할 수 있다.

* 확장 함수는 기존에 구현된 클래스를 전혀 건드리지 않고 기능을 확장할 수 있다.

* 확장 함수를 정의하기 위해서는 수신 객체의 타입을 함수 이름 앞에 적기만 하면 된다.

* 확장 함수를 Java 에서 호출할 때에는 'static(정적) 메소드'를 호출하는 것과 같다.

* 확장 함수는 실제로는 static 함수이며, 코틀린에서 멤버 ㅁ소드처럼 사용할 수 있게 해주는 문법적 편의이다.

* 확장 함수는 실제로는 외부에 선언된 static 함수이기 때문에, 상속되거나 오버라이드할 수 없다.

 

 

- 널 안전성( Null Safety )

- Type : Type 과 같은 타입만 허용. NULL 허용하지 않음.

- Type? : Type OR NULL

// 코틀린 타입은 기본적으로는 널(NULL) 을 허용하지 않는다.
fun strLenNonNull(str: String): Int {
    // 파라미터로 받은 str 은 널이 될수 없으므로 안전하다.
    return str.length
}

// 만일 널(NULL) 가능성이 있다면 타입에 ?를 붙여야 한다.
fun strLenNullable(str: String?): Int {
    // 널 가능성이 있는 str 메소드에 접근하면 에러가 발생한다.
    //return str.length
    
    if( str != null ) {
        // 널체크 이후 str 은 String? 타입에서 String 타입으로 스마트 캐스팅된다.
        return str.length
    }else{
        return 0
    }
}

 

- 안전한 호출 연산자 ?.

- 엘비스 연산자 ?:

// 문자열에 끝 Char 를 반환한다
fun strLastCharNullable(str: String?): Char? {
    // ?. 연산자는 str 이 NULL 이면 null 이 반환된다.
    return str?.get(str.length - 1)
}

// 문자열에 끝 Char 를 반환한다
fun strLastCharNullable(str: String?): Char {
    // ?. 연산자는 str 이 NULL 이면 "".single() 이 반환된다.
    return str?.get(str.length - 1) ?: "".single()
}

 

- 코틀린은 수신 객체가 널이 아닌 경우에만 실행되는 let 함수

// let 함수를 이용한 예제
fun strPrintLen(str: String?) {
    // let 함수는 수신객체인 str 이 널이면 실행되지 않는다.
    str?.let { print(strLenNonNull(it)) }
}

 

- as?

- as : 타입 캐스팅 연산자

class Test(val id: Int) {

    // equals 를 오버라이드 함, id 가 같으면 같은 객체로 취급
    override fun equals(other: Any?): Boolean {
        // as? 연산자를 사용하면 타입이 같은 경우 캐스팅이 정상적으로 되고
        // 캐스팅이 실패하면 null 이 반환된다.
        // null 이 반환된 경우 엘비스 연산자의 디폴트 식이 실행되어 false 가 리턴된다.
        val otherTest = other as? Test ?: return false
        
        // otherTest 은 스마트캐스팅 되어 널을 신경쓸 필요가 없다.
        return Test.id == id
    }
}

 

* 널(Null)은 Java 에서 객체가 할당되지 않은 상태 를 의미한다.

* NPE( Null Pointer Exception ) 는 Java 에서 객체가 널 상태여서 발생하는 예외를 의미한다.

* Java 의 레퍼런스 타입은 널을 할당할 수 있지만, 널은 해당 타입의 메소드를 실행할 수 없으며 완전히 다른 타입이다.

* Java 에서 NPE 가 자주 발생하는 이유는 Java 의 레퍼런스 타입은 모두 널일 수 있지만 전부 널 체크를 하기 위해서는 코드가 복잡해지기 때문이다.

* 코틀린은 타입 시스템 자체에서 널 가능성을 체크한다.

* 코틀린의 Type 은 기본적으로 널을 할당할 수 없으며, 널 가능성이 있다면 Type?로 선언해야 한다.

* 코틀린은 널을 처리하기 위하여 여러 편리한 연산자를 제공한다.

 

 

반응형

'IT > Android(비공개)' 카테고리의 다른 글

[Android] View / Theme  (0) 2020.03.05
Activity / Intent  (0) 2020.03.04
Android-studio / svn 연결  (0) 2020.03.04
3. Kotlin(코틀린)_2  (0) 2020.02.26
2. Kotlin(코틀린)_1  (0) 2020.02.20
1. Android 기본  (0) 2020.02.07
Android 재시작  (0) 2020.02.07