본문 바로가기

TIL

TIL) KClass, Sealed class, 고차 함수: 함수 타입, invoke

1. KClass

 

java 에는 Class 라는 타입(클래스) 가 있듯이, 

 

Kotlin 에는 KClass 라는 타입이 있다.

 

1. KClass 타입
val stringType = String::class

2. Class 타입
val stringType = String::class.java

 

KClass 타입도 Class 타입과 마찬가지로, 여러가지 함수를 제공한다.

 

 

프로퍼티

사용 반환타입 설명
constructors Collection<KFunction<T>> 선언된 생성자의 Collection
isAbstract Boolean 추상 클래스 여부
isCompanion Boolean Compaion object 클래스 여부
isData Boolean Data 클래스 여부
isFinal Boolean 클래스가 final 인지 여부
isFun Boolean kotlin functional interface 인지 여부
isInner Boolean inner class 여부
isOpen Boolean open 인지 여부
isSealed Boolean sealed 클래스 여부 (이글 2번쨰 주제에 설명)
사용 반환타입 설명
isValue Boolean value (값) 클래스인지 여부
members Collection<KCallable<*>> 접근 가능한 모든 함수와 프로퍼티. 해당 클래스와 상위 클래스를 포함. 생성자는 미포함.
nestedClasses Collection<KClass<*>> 내부 중첩 클래스의 Collection
objectInstance T? object 클래스라면 해당 객체를 반환 (object 클래스는 싱글톤이므로)
qualifiedName String? FQCN
sealedSubclasses List<KClass<out T>> 클래스의 하위 타입 중 sealed 클래스의 리스트
simpleName String? 패키지 경로가 포함되지 않은 클래스 이름
supertypes List<KType> 클래스의 상위 타입의 리스트
typeParameters List<KTypeParameter> 타입 파라미터의 리스트 (제네릭 클래스 타입 (T, V 같은거))
사용 반환타입 설명
visibility KVisibility? 가시성 (PUBLIC, PRIVATE ..)
anotations List<Annotation> 클래스에 붙은 에너테이션 리스트

 

외에도,  멤버 함수로 isInstance(value: Any?) 처럼 어떤 객체가 해당 KClass 타입인지 검사 할 수 있는 함수도 있고,

여러 확장 프로퍼티와 확장 함수들이 있다.

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/

 

KClass - Kotlin Programming Language

 

kotlinlang.org

 

 

이 중에 createInstance() 메서드로 KClass 객체를 해당 클래스의 인스턴스로 만들 수 있다.

val stringType = String::class

stringType.createInstance() // 객체 생성

 

생성자가 없거나, 기본값이 있는 생성자가 있는 경우에만 만들어 준다.

 

기본값이 없고, 생성자가 인수를 받아야 할 때에는 IllegalArgumentException 이 발생한다.

https://www.baeldung.com/kotlin/kclass-new-instance

 

 

 

 

 

2. Sealed class

open 클래스를 상속 받는 하위 클래스는 여러 파일에 존재할 수 있다. 따라서 관리하기 힘들다. 컴파일러가 얼마나 많은 하위 클래스가 있는지 알지도 못한다.

 

sealed 키워드도 해당 클래스를 open 처럼 상속을 할 수 있게 만들어준다.

차이점은, 동일 파일에 정의된 하위 클래스 외에 다른 하위 클래스는 존재하지 않는다는 것을 컴파일러에게 알려준다. 

즉, 동일 파일에서만 상속할 수 있는 클래스를 만들어 준다. 

코드상으로는 private 생성자를 가진 abstract 클래스로 만들어 준다.

 

하위 클래스가 될 수 있는 클래스를 제한하여 얻을 수 있는 이점 중 하나는 when 절에서 else 를 사용하지 않아도 된다는 것이다.

반대로, else 가 없기 때문에 when 절에 모든 하위 클래스 타입을 명시하지 않으면 에러가 발생한다.

 

enum 으로도 else 없는 when 절을 만들 수 있지만, enum 클래스의 상수들은 singleton 객체이지만,

sealed 클래스의 하위 타입들은 필요하면 여러 객체를 만들 수 있다.

 

https://codechacha.com/ko/kotlin-sealed-classes/

 

Kotlin - Sealed class 구현 방법 및 예제

Sealed class는 Super class를 상속받는 Child 클래스의 종류 제한하는 특성을 갖고 있는 클래스입니다. Enum과의 차이점은 Enum은 single instance만 만들 수 있는 반면에 Sealed class는 여러개의 객체를 생성할

codechacha.com

 

 

 

 

 

3. 고차 함수: 함수 타입

class a : (String) -> String {

...

 

위 a 라는 클래스는 (String) -> String 이라는 것을 구현했다.

 

인터페이스도 아닌 것이 클래스도 아니다.

 

이는 함수 타입 이다.

 

이 함수 타입은 컴파일 시 인터페이스로 바뀐다.

구체적으로 Function0<R> (인자가 없는 함수), Function1<P1, R> (인자가 하나만 있는 함수 ) 와 같은 인터페이스 타입으로 바뀐다.

 

이 FunctionN 인터페이스는 공통적으로 invoke() 라는 하나의 메서드가 정의되어 있다.

(String) -> String 과 같은 함수 타입의 람다 식이 이 invoke() 의 구현부로 들어가게 되고,

invoke() 를 실행하면 함수 타입이 실행되게 되는 것이다.

 

한마디로,

람다 = invoke() 를 가진 인터페이스 이다.

 

 

4. invoke()

invoke() 는 코틀린에서 예약어처럼 정해진 특별한 연산자이다.

 

연산자라고 했듯이 operator 키워드가 앞에 붙고, 함수의 이름이 invoke() 라면, 특별한 능력을 얻는다.

 

class A {
    operator fun invoke(str: String): String {
        return str.toUpperCase()
    }
}

fun main() {
    val res = A()("tomas")
    println(res) // TOMAS(대문자)
}

 

operator fun invoke 를 선언했다.

그리고 A() 로 객체를 만들고, A().invoke("tomas") 가 아니라 invoke 를 생략한 A()("tomas") 와 같이 실행했다.