안드로이드 개발 (42) Kotlin

(이전 에피소드 반복)

https://gift123.77 See More

Android 개발(41) Kotlin 사용 – 요약 – 5월 5일

(이전 파트 다시 보기) Android 개발 (40) 실행 중인 Kotlin 요약 – 4 (이전 파트 다시 보기) Android 개발 (38) 실행 중인 Kotlin 요약 – 2 (이전 파트 다시 보기) Android 개발 (37) 실행 중인 Kotlin 요약 – 분야에서 1

gift123.tistory.com

6장의 요약입니다.

참고 실제 Kotlin에 대한 요약이 아니라 개인적으로 필요한 사항에 대한 요약입니다.

6장 코틀린 타입 시스템


Kotlin 고유의 유형 시스템에는 항상 유형이 nullable인지 여부에 따라 달라지는 보안, 변경 가능한 유형과 변경 불가능한 유형으로 구분, 래퍼 유형과 기본 유형의 구분 등 Java smarter에서 처리해야 하는 기능이 있습니다.

*고독한 생각: kotlin은 위의 이점과 함께 매우 우수한 보안을 보여줍니다.

*

1) 무효

– 안전한 전화 교환원?. null 사례를 처리하기 위해 구문적으로 깨끗하게 사용됩니다.

예를 들어, if(s !
= null) s.toUpperCase() else null은 s?.toUpperCase()로 간결하게 표현할 수 있습니다.

//?. 처리가 없다면 아래와 같은 코드로 해야한다.

if(s !
= null) s.toUpperCase() else null //?. 를 사용한다면 아래처럼 매우 간결한 코드로 null 처리를 매우 효율적으로 할 수 있다.

s?.toUpperCase()

-티? T가 null이고 null을 반환하지 않고 다른 값을 반환하려는 경우 Elvis 연산자 ?:를 사용할 수 있습니다.

– 어떻게? R은 T를 R 유형으로 캐스트할 수 없는 경우 null을 반환하는 안전한 캐스트입니다.

//아래 두개의 코드는 같은 동작을 한다.

//1) foo as?Type //2) if(foo is Type) foo as Type else null

– 기타 null 처리는 let?입니다.

{} , Latini, 확장 기능이 있습니다.

B.컬렉션.isNullOrEmpty() 등

– 모든 유형 매개변수는 기본적으로 null일 수 있습니다.

fun <T>test(t:T){
//위에서 T? 를 명시하지 않았음에도 불구하고 T는 될 수 있어서
// 안전한 호출자 ?. 를 사용해도 Ide의 조언이 없다.

t?.hashCode() }

– 타입 매개변수가 null이 되지 않도록 하려면 타입의 상한을 nullable이 아닌 타입으로 지정해야 합니다.

//T에 Any로 타입 상한을 하면 T는 null이 될 수 없으므로, 안전한 호출자 ?. 를 쓸 이유가 없다.

fun <T:Any>test(t:T){ t.hashCode() }

*외로운 생각: Null을 구문적으로 관리하는 데 매우 훌륭하다고 생각합니다.

특히 Elvis 연산자는 책에서 잠깐 언급했지만 현장에서 실제로 잘 사용하고 있습니다.

*

– 플랫폼 타입은 코틀린이 제로 정보를 알 수 없는 타입이므로 매우 위험하다.

따라서 원래 플랫폼 유형에서 null 레이블을 처리하거나(Java에서는 @Nullable 주석이 예임) 문서를 구성하여 공유해야 합니다.


카운트:인트!
null을 입력해도 컴파일에는 문제가 없습니다.

– Kotlin이 모든 플랫폼 유형을 nullable 유형으로 취급하는 대신 플랫폼 유형의 개념을 사용하는 이유는 외부 유형이 모두 null이면 null을 확인하는 비용이 상당히 높기 때문에 플랫폼 유형의 가능성이 열려 있기 때문입니다.

. 일반적으로 ArrayList? 를 사용하는 경우 배열 요소에 액세스할 때마다 null 검사 및 안전한 캐스트를 수행해야 합니다.

*외톨이의 생각: Java와 Kotlin을 사용하는 프로젝트에서 눈에 띄는 실수를 할 수 있는 문제입니다.

따라서 두 언어를 함께 사용한다면 플랫폼 유형을 철저히 관리해야 합니다.

*

– Kotlin에서 Java 메서드를 재정의할 때 메서드의 매개변수 또는 반환 유형을 nullable로 선언할지 여부를 결정할 수 있습니다.

//아래와 같은 자바 클래스가 있다면
public abstract class JavaTest {
    public abstract Integer addValue (Integer addValue);
}
class JavaTestImpl() : JavaTest() {
//1) addVlue의 파라미터가 널을 허용하지 않도록 지정 가능
    override fun addValue(addValue: Int): Int {
        return addValue + 1
    }
}


class JavaTestImpl() : JavaTest() {
//2) addVlue의 파라미터가 널을 허용하도록 지정 가능
    override fun addValue(addValue: Int?):Int {
        return addValue + 1
    }
}

class JavaTestImpl() : JavaTest() {
//3) addVlue의 return 타입이 널을 허용하도록 지정 가능 
    override fun addValue(addValue: Int):Int? {
        return addValue + 1
    }
}


// 등등.. 상속받은 자바 클래스의 파라미터 나 리턴 타입을 언제든지 널 허용/비허용을 바꿀 수 있다.

2) Kotlin의 기본 유형

Kotlin에서 개발자는 기본 유형과 참조 유형을 직접 구분하지 않습니다.

내부적으로는 구문 상황에 따라 Kotlin 컴파일러가 기본 유형을 사용할지 참조 유형을 사용할지 상황에 따라 지능적으로 사용합니다.

– Kotlin으로 개발하는 개발자는 Java와 같은 기본 유형 대신 참조 유형을 사용할 걱정 없이 한 번에 하나의 유형만 사용할 수 있습니다.

//코틀린에서 아래와 같이 사용한다면,, 
val i:Int = 1
val list:List<Int> = listOf(1,2,3)
 // 자바로 변환하면 아래와 같이 변한다.

// 아래는 원시 타입인 int를 사용하고 int i = true; // Collection<T> 와 같이 참조 타입이 필요한 경우 Integer 타입을 사용하는것을 확인할 수 있다.

List list = CollectionsKt.listOf(new Integer(){1, 2, 3});

위와 같이 내부 컴파일러는 상황에 따라 기본형과 참조형을 구분하므로 개발자가 따로 구분할 필요는 없다.

– Java 프리미티브 유형은 Kotlin에서 사용될 때 (플랫폼 유형이 아닌) null을 허용하지 않는 유형으로 처리될 수 있습니다.

– null이 될 수 있는 Kotlin 유형은 Java 기본 유형으로 표현할 수 없습니다.

따라서 Kotlin에서 null일 수 있는 기본 유형을 사용하면 해당 유형이 Java 래퍼 유형으로 컴파일됩니다.

– Java와 Kotlin 모두에서 일반 클래스는 상자 유형을 사용합니다.

– Kotlin의 잊어버리기 쉬운 기본 유형 리터럴은 double 유형에서 1.2e10, 1.2e-10으로 표현됩니다.

– Kotlin의 잊어버리기 쉬운 기본 유형 리터럴은 0x 또는 0X 접두사가 있는 16진수 리터럴입니다: 0xCAFEBABE, 0xbcdL

– Kotlin의 잊어버리기 쉬운 기본 유형 리터럴은 접두사가 0b 또는 0B인 이진 리터럴입니다: 0b000000101

– kotlin은 숫자 리터럴(1_123, 1_000_000) 중간에 밑줄을 넣을 수 있습니다.

– Any는 Kotlin에서 null을 허용하지 않는 최상위 유형입니다.

(Nullability의 경우 Any?) 이것은 모든 유형, 프리미티브 또는 참조의 조상 유형입니다.

//Any는 아래 3가지 메서드를 가지고 있다.

public open class Any { public open operator fun equals(other: Any?): Boolean public open fun hashCode(): Int public open fun toString(): String }

– Java의 Any와 유사하게 Object가 있지만 참조 유형만 유형 계층 구조가 되며 Object가 정점이 됩니다.

단위 유형은 void와 유사하지만 다릅니다.

단위는 형식 인수로 사용할 수 있습니다.

Kotlin 함수는 반환 유형이 지정되지 않은 경우 묵시적으로 Unit을 반환하며, 별도의 반환을 지정하지 않아도 Unit을 반환합니다.

//Unit은 싱글톤 으로 구현 되어 있음
public object Unit {
    override fun toString() = "kotlin.Unit"
}

– 아무 것도 정상적으로 종료되지 않는다는 의미입니다.

public class Nothing private constructor()

/*loner는 다음과 같이 생각합니다.

실제로 위 줄의 끝은 없습니다.

클래스로 생성했는데 생성자가 비공개라서 생성이 안되네요. 컴패니언 객체가 없기 때문에 호출할 내부 인스턴스가 없으며 객체가 아닌 클래스이기 때문에 단일 인스턴스로 사용할 방법이 없습니다.

그래서 함수에서 반환형으로 사용할 때 Nothing new를 생성하거나 호출하는 단일 인스턴스가 없기 때문에 throw와 같이 함수 내부에 프로그램 종료를 작성할 수밖에 없습니다.

*/

fun nothingTest(): Nothing {
    throw RuntimeException("이 함수의 끝에 throw말고 적을 수 있는게 없다.

.") }

3) 컬렉션과 배열

무효의 네 가지 주요 경우가 있습니다.

수집? , 수집 수집?, 수집 사용되는 null 기능, null 검사 빈도, 구문 처리 등에 따라 다릅니다.

/* 외로운 생각: 개인 소장품? Collection이 빈 케이스를 보여줄 때 Collection으로 타입을 정의한 후 초기값으로 emptyList()를 사용하는 경우가 많은 것 같다.

지정되었습니다.

?. 에 따라 null 검사 횟수도 제공하고 빈 컬렉션임을 보다 명시적으로 나타내기 때문입니다.

*/

– 컬렉션의 종류는 각각 Collection과 MutableCollection으로 구분되며, 읽기전용과 가변 컬렉션으로 구분되어 상황에 따라 효율적으로 수정이 가능합니다.

읽기 전용으로 변경 지점과 이동하려는 지점을 명확하게 지정하여 읽을 수 있습니다.

/* 외로운 생각: 아래 코드는 최상위 Kotlin.collections 파일에 작성된 Collection의 핵심 코드입니다.

*/

package kotlin.collections

import kotlin.internal.PlatformDependent

public interface Iterable<out T> {
    public operator fun iterator(): Iterator<T>
}

public interface MutableIterable<out T> : Iterable<T> {
    override fun iterator(): MutableIterator<T>
}

public interface Collection<out E> : Iterable<E> {
    public val size: Int

    public fun isEmpty(): Boolean

    public operator fun contains(element: @UnsafeVariance E): Boolean

    override fun iterator(): Iterator<E>

    public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}


public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
    override fun iterator(): MutableIterator<E>
    
    public fun add(element: E): Boolean

    public fun remove(element: E): Boolean

    public fun addAll(elements: Collection<E>): Boolean

    public fun removeAll(elements: Collection<E>): Boolean

    public fun retainAll(elements: Collection<E>): Boolean

    public fun clear(): Unit
}

public interface List<out E> : Collection<E> {

    override val size: Int
    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>

    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
    
    public operator fun get(index: Int): E

    public fun indexOf(element: @UnsafeVariance E): Int
    public fun lastIndexOf(element: @UnsafeVariance E): Int

    public fun listIterator(): ListIterator<E>

    public fun listIterator(index: Int): ListIterator<E>

    public fun subList(fromIndex: Int, toIndex: Int): List<E>
}

public interface MutableList<E> : List<E>, MutableCollection<E> {

    override fun add(element: E): Boolean
    override fun remove(element: E): Boolean
    override fun addAll(elements: Collection<E>): Boolean
    public fun addAll(index: Int, elements: Collection<E>): Boolean

    override fun removeAll(elements: Collection<E>): Boolean
    override fun retainAll(elements: Collection<E>): Boolean
    override fun clear(): Unit

    public operator fun set(index: Int, element: E): E

    public fun add(index: Int, element: E): Unit
    
    public fun removeAt(index: Int): E

    override fun listIterator(): MutableListIterator<E>

    override fun listIterator(index: Int): MutableListIterator<E>

    override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}

public interface Set<out E> : Collection<E> {
    override val size: Int
    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>

    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}

public interface MutableSet<E> : Set<E>, MutableCollection<E> {

    override fun iterator(): MutableIterator<E>

    override fun add(element: E): Boolean

    override fun remove(element: E): Boolean

    override fun addAll(elements: Collection<E>): Boolean
    override fun removeAll(elements: Collection<E>): Boolean
    override fun retainAll(elements: Collection<E>): Boolean
    override fun clear(): Unit
}

public interface Map<K, out V> {

    public val size: Int

    public fun isEmpty(): Boolean

    public fun containsKey(key: K): Boolean

    public fun containsValue(value: @UnsafeVariance V): Boolean

    public operator fun get(key: K): V?

    @SinceKotlin("1.1")
    @PlatformDependent
    public fun getOrDefault(key: K, defaultValue: @UnsafeVariance V): V {
        return null as V
    }
    
    public val keys: Set<K>
    public val values: Collection<V>
    public val entries: Set<Map.Entry<K, V>>
    public interface Entry<out K, out V> {
        public val key: K
        public val value: V
    }
}

public interface MutableMap<K, V> : Map<K, V> {

    public fun put(key: K, value: V): V?

    public fun remove(key: K): V?
    
    @SinceKotlin("1.1")
    @PlatformDependent
    public fun remove(key: K, value: V): Boolean {
        return true
    }

    public fun putAll(from: Map<out K, V>): Unit

    public fun clear(): Unit

    override val keys: MutableSet<K>

    override val values: MutableCollection<V>

    override val entries: MutableSet<MutableMap.MutableEntry<K, V>>

    public interface MutableEntry<K, V> : Map.Entry<K, V> {
        public fun setValue(newValue: V): V
    }
}

/* 외로운 생각: 모든 컬렉션 유형은 인터페이스로 구현되며 Iterable로 시작하여 Collection을 구현하는 계층 구조를 생성했음을 알 수 있습니다.

다양한 클래스가 사용됩니다.

일반적인 예로는 ArrayList, HashSet 및 LinkedHashMap이 있습니다.

ArrayList는 확실히 MutableList를 구현합니다.

HashSet은 확실히 MutableSet을 구현합니다.

LinkedHashMap은 MutableMap을 구현합니다.

.
*/

//MutableList를 구현 한다.

expect class ArrayList<E> : MutableList<E>, RandomAccess { /*...*/ } //MutableSet을 구현 한다.

expect class HashSet<E> : MutableSet<E> { /*...*/ } //MutableMap을 구현 한다.

expect class LinkedHashMap<K, V> : MutableMap<K, V> { /*...*/ }

/* 외로운 생각: 따라서 Kotlin이 Java와 동일한 컬렉션을 사용할 수 있는 이유는 위와 같이 인터페이스에서 정의한 유형에 따라 필요한 함수만 사용하고, 함수의 경우 위와 같은 계층 구조를 가지므로 제한을 두거나 사용하기 때문입니다.

* /

– 읽기 전용 컬렉션이 항상 멀티스레딩에 안전하다는 보장은 없습니다.

Kotlin의 컬렉션은 읽기 전용과 변경 가능한 형식을 모두 사용하는데 상황에 따라 읽기 전용과 변경 가능한 형식이 동시에 동일한 컬렉션 개체를 참조하는 경우 문제가 발생할 수 있습니다.

예를 들어 읽기 전용 컬렉션에 사용하고 변경 가능한 컬렉션에서 컬렉션 내용을 변경하면 다중 스레딩 오류가 발생할 수 있습니다.

– Java와 Kotlin을 함께 사용하는 프로젝트에서 Java에는 읽기 전용 및 수정 가능 개념이 없으므로 Kotlin 컬렉션을 Java 메소드 인수로 전달할 때 주의하십시오. (코틀린에서는 자바 메서드에 인수를 읽기 전용 컬렉션으로 전달했는데, 자바 메서드가 가변으로 쓰면 읽기 전용 컬렉션의 내용이 수정된다.

)

– 코틀린이 플랫폼 타입에서 자바 슈퍼클래스를 구현하고 메소드를 오버라이딩하여 구현하는 경우 컬렉션 타입인 파라미터도 개발자가 원하는 대로 읽기전용 또는 읽기전용으로 변경할 수 있다.

– 배열에서 기본 유형을 사용하려는 경우 배열이 지원되므로 IntArray, ByteArrary 및 CharArray와 같은 기본 유형을 사용할 수 있습니다.