퀴즈 :
1. 탑레벨의 고차 함수 TimeLogCheck 를 만든다.
2. TimeLogCheck 는 String 타입의 tag 와 함수타입 (tag:String) -> List<String> 을 가지는 f 를 인자로 갖는다.
3. TimeLogCheck 에서 인자로 받은 람다를 실행하기 전에 “현재시간 : tag -> 시작” 이란 로그를 출력하고 람다를 실행한 후에 “현재시간 : tag -> 종료(elapsed time : 종료 – 시작)” 을 출력한다.
4. TimeLogCheck 는 람다함수가 리턴한 값을 리턴한다.
5. 순서대로 1~1000 까지 가지는 mutableList<Int> 타입의 initList 를 생성한다.
6.
A : 리스트의 filter 로 짝수를 걸러내고, map 에서 Int 를 String 으로 바꾼 String 리스트를 만든다.
B : 리스트를 asSequence 를 통해 sequence 로 바꾸고 filter 로 짝수를 걸러내고, map 에서 Int 를 String 으로 바꾼 후 toList 이란 종단 함수를 통해 String 리스트를 만든다.
8. A와 B 를 timeLogCheck 를 통해 람다로 실행하여 실행시간 로그가 나오도록 한다. (마지막 인자가 람다일때 () 밖으로 람다를 빼는 방법으로 작성한다)
* 시간 관련 코드는 아래를 참조한다.
var endTime = GregorianCalendar()
println("${endTime.toZonedDateTime()} : $tag -> End (elapsed time : ${endTime.timeInMillis - startTime.timeInMillis})")
- 출력
2018-07-05T14:16:56.161+09:00[Asia/Seoul] : collection -> Start
2018-07-05T14:16:56.249+09:00[Asia/Seoul] : collection -> End (elapsed time : 88)
2018-07-05T14:16:56.253+09:00[Asia/Seoul] : stream -> Start
2018-07-05T14:16:56.275+09:00[Asia/Seoul] : stream -> End (elapsed time : 22)
# Kotlin - 제너릭 프로그래밍
- 제너릭(C++ template) 프로그래밍
* 타입을 미리 정의하지 않고 하는 프로그래밍
* 인스턴스화 하는 시점에 타입 파라미터<>를 통해 타입 지정
* 타입 파라미터를 통해 여러 타입으로 사용할 수 있는 객체, 함수 생성 : 하나의 코드로 다양한 타입을 처리할 수 있다.
* 여러 타입을 처리해야할 때 Any (Java Object 클래스) 타입의 문제
1. 캐스팅이 필요했지만 제너릭은 불필요
2. 실수로 다른 타입을 전달했을 경우 오류 찾는것이 힘들었지만 제너릭은 사전에 오류를 발생시킴
# Kotlin - 제너릭 클래스
class ClassName<T, S> (var v1: T? = null, var v2: S? = null)
* 클래스 타입 = 클래스이름 : 원시타입 + T,S 제너릭 타입
- <> 안에 타입 파라미터 정의
- 타입 파라미터가 하나 이상일 때 콤마(,)로 구분하여 나열
- 타입추론이 가능할 경우 생략 가능
* listOf<String>("a","b","c") -> listOf("a", "b", "c")
* 추론이 안될 경우 명시적으로 선언해야함.
* 원시타입만으로 사용 불가 (Java는 하위 호환을 위해 가능)
- 인스턴스화 시킬 때는 타입파라미터(제너릭)를 넣어줌으로써 된다!
var a = 클래스이름<Int, String> // 타입인자 : <Int, String>
data class TData<T, S> (var v1: T? = null, var v2: S? = null)
val d1 = TData("delay", 23) // <String, Int>
println("d1 : $d1")
val d2 = TData("delay2", 23+10) // <String, Int>
println("d2 : $d2")
val d3 = TData(20, d2) // <Int, TData<String, Int>>
println("d3 : $d3")
- 출력
d1 : TData(v1=delay, v2=23)
d2 : TData(v1=delay2, v2=33)
d3 : TData(v1=20, v2=TData(v1=delay2, v2=33))
# Kotlin - 타입파라미터 : 명명 관례
- 타입 파라미터 이름은 마음대로 지정할 수 있지만 관례가 있음.
- 관례
* T : Type(기본 값)
* S, U, V etc. : 2nd, 3rd, 4th types (T 외에도 타입 파라미터가 여러개 필요할 때)
* E : element - 자바 컬렉션 프레임워크에서 많이 사용
* K : Key - Map 선언할 때
* V : Value - Map 선언할 때
* N : Number
# Kotlin - 제너릭 함수/프로퍼티
- 제너릭 함수
fun <T> newList(vararg ts: T):ArrayList<T>
* <T> : 타입 파라미터
- 함수를 사용하는 시점에서 특정 타입으로 지정
newList<Int>(1,2,3,4,5)
newList("a","b","c") // 타입 추론
- 제너릭 프로퍼티
* 확장된 프로퍼티만 제너릭 설정 가능
val <T> List<T>.penultimate: T
- 근데 위에꺼 쳐보면 not allowed 뭐시기 나온다. 그래서 코틀린 공식 홈페이지 문서 참조.
* 일반 프로퍼티는 제너릭 불가 : val <T> x:T? = null // 불가
# Kotlin - 서브타입, 슈퍼타입
- class subClass : superClass
* subClass 타입
* superClass 타입도 가능
- 상속 관계에 있는 타입
* 자식 입장에서 부모 : 슈퍼타입
* 부모 입장에서 자식 : 서브타입
- 인터페이스나 abstract도 자식입장에서는 슈퍼타임
- 상속관계에서 서브타입의 모든 슈퍼타입은 자신의 타입
# Kotlin - 원시타입, 제너릭타입
- class ClassName<T>
ClassName : 원시 타입
T : 제너릭 타입
ClassName<T> : 클래스 타입
- 두 클래스 타입이 서브타입이 되려면
제너릭 타입이 같아야함
원시타입이 서브타입 관계여야 함.
예외 ) 변성
- ArrayList<Int> 는 List<Int> 의 서브 타입이다.
제너릭 타입이 Int 로 같고, ArrayList 는 List 를 상속
- ArrayList<Number> 와 List<Int> 는 서브타입이 아니다.
제너릭 타입인 Int 가 Number 를 상속하고 있지만 서로 다른 타입이다.
제너릭 타입이 같지 않으면 ArrayList 와 List 관계는 무의미
List<Int> != List<Number> //제너릭타입이 달라서 다름
List<Int> = ArrayList<Int> // 할당 가능. 부모로 자식을 가리킬 수 있지만, 자식으로 부모를 가리킬 수 없다.
# Kotlin - 타입 파라미터 제한
- 기본적으로 타입 인자는 모든 타입 사용 가능
- <T: Number> 로 타 입력 파라미터에 Upper Bound 제한 가능
* String 못들어감
* Number 를 포함한 서브타입만 인자로 사용 가능
* 내부에서 제한한 타입의 멤버 접근 가능
# Kotlin - 타입 소거
- 제너릭으로 지정된 타입의 매개변수 정보는 바이트 코드에 수록하지 않고 소거
* 소거 : List<Int>, List<String> -> List<Object> (자바는 런타임 때 이렇게 바뀜)
- 따라서 타입 매개변수가 지정된 클래스의 객체 타입을 런타임중 검사할 수 없음
* 해당 타입 파라미터들을 파라미터로 갖는 함수 오버라이드 불가
* 제너릭 타입은 해당 타입의 is 연산 불가. 타입이 소거되었기 때문(이게 가장 큰 단점. 현재는 편법으로 해결)
- 예외적으로 함수의 경우 inline 과 함께 타입 파라미터에 reified 키워드로 실체화 가능
* is 연산이 가능해짐
* 해당 함수는 자바에서 사용 불가
# Kotlin - 변성(variance)
- ArrayList<Int> vs List<Int>
* ArrayList<Int> 는 List<Int > 의 서브타입
- List<Int> vs List<Number>
* 제너릭 타입 Int 가 제너릭 타입 Number 의 서브타입이지만 List<Int> 는 List<Number> 의 서브타입 아님. 제너릭이 다르기 때문!
* 하지만 List<Int> 는 List<Number> 의 서브타입임.
* 리스트는 타입매개변수의 서브타입을 인자로 받게 정의되어 있음
- 타입 매개변수와 타입 인자 간의 서브 타입 관계 설정을 변성(변형) 이라 함
* 제너릭 타입의 변환이 생김
- 변성은 클래스나 인터페이스의 타입 매개변수에만 설정 가능
* 함수 매개변수나 반환타입에는 지정 불가
# Kotlin - 변성(variance) : 불변성/무공변성
- 타입 매개변수에 아무것도 하지 않으면 불변성(무공변성)
* default는 불변성(무공변성)
- 제너릭 타입이 일치해야한다.
- 제너릭 타입의 상속관계 허용 안됨.
interface IMyClazz<T>
open class MyClazzImpl<T>(val v: T) : IMyClazz<T>
data class MySubClazzImpl<T>(val w: T) : MyClazzImpl<T>(w)
fun printMyClazz(my: MyClazzImpl<Number>) {
println(my)
}
val m1 = MyClazzImpl<Int>(1)
val m4 = MyClazzImpl<Number>(222)
val m5 = MySubClazzImpl<Number>(222)
println(m1)
printMyClazz(m4)
//printMyClazz(m1)
printMyClazz(m5)
# Kotlin - 변성(variance) : 공변성(공변형)
- 타입 매개변수에 out 변형 제한자를 지정
* 자바의 super 변형 제한자와 같다 : 제너릭 타입이 상속관계에 있으면 파라미터로 받아준다.
public interface List<out E> : Collection<E>
- E 타입을 포함하여 E 의 서브 타입도 타입인자가 가능
- List<Int> 는 List<Number> 의 서브
* 부모(Number)한테는 받아줄 수 있는 것
# Kotlin - 변성(variance) : 불변성/무공변성
- 타입 매개변수에 in 변형 제한자를 지정
* 자바의 extends 변형 제한자와 같다
* 공변성의 반대
public interface Comparable<in T>
- T 타입을 포함하여 T 의 수퍼 타입도 타입인자가 가능
* 잘 사용되지 않음.
class InVariance<T>
class CoVariance<out T>
class ContraVariance<in T>
val in1 = InVariance<Int>()
// val in2:InVariance<Number> = in1
val co1 = CoVariance<Int>()
val co2: CoVariance<Number> = co1
val ct1 = ContraVariance<Int>()
// val ct2 : ContraVariance<Number> = ct1
val ct3 = ContraVariance<Number>()
val ct4:ContraVariance<Int> = ct3
# Kotlin - 변성(variance) : 사용지점 변성
- 불변형의 경우 사용할때 in/out 을 강제 지정해서 사용 가능
* Interface 나 class 에 지정하는것 : 선언 지점 변성(변형)
* 사용할때 지정 : 사용되는 지점에서 변성(변형)
- 공변형이나 반공변형으로 선언된 경우 불가 (충돌)
class InVariance<T>
class CoVariance<out T>
class ContraVariance<in T>
val in1 = InVariance<Int>()
// val in2:InVariance<Number> = in1
val in3:InVariance<out Number> = in1 // 사용지점변성
# Kotlin - 변성(variance) : 스타 프로젝션
- 원시타입만 사용 불가(자바는 가능)
* List<Number> : 가능
* List : 불가능
- 스타 프로젝션
* List<*>
* 타입 인자를 알 수 없음을 의미
* List<Any?> 와 다름
+ Any 타입을 받겠습니다!
+ <out Any> : Any가 가지고 있는 속성을 쓸 수 있다. 하지만 *는 쓸 수 없다!
*타입이 중요하지 않은 함수에 유용
class Star<T>
val st1:Star<Any> = Star<Any>()
// val st2:Star<Any> = Star<Int>()
val st5:Star<out Any> = Star<Int>()
val st6:Star<*> = Star<Any>()
val st7:Star<*> = Star<Int>()
fun printListItem(list : List<*>) {
println("갯수 : ${list.size}")
println("내역 : $list")
// list.add(100) // list…여야만 쓸 수 있어서
}
val ls1 = listOf(1,2,3)
val ls2 = listOf("a", "b", "c")
printListItem(ls1)
printListItem(ls2)
# 출처 : 백지훈 모베란 대표이사님
'Programming > Kotlin' 카테고리의 다른 글
[Kotlin]코틀린을 이용한 안드로이드 프로그래밍 실습 03 (0) | 2018.07.04 |
---|---|
[Kotlin]코틀린을 이용한 안드로이드 프로그래밍 실습 02 (0) | 2018.07.03 |
[Kotlin] 코틀린을 이용한 안드로이드 프로그래밍 실습 01-2 (0) | 2018.07.02 |
[Kotlin] 코틀린을 이용한 안드로이드 프로그래밍 실습 01-1 (0) | 2018.07.02 |