Java 애너테이션 프로세싱에서 KSP로의 참조
이 문서는 Java 애너테이션 프로세싱(APT) API의 기능 하나하나를 KSP로 어떻게 구현할 수 있는지 보여 주는 참조 자료예요. 표의 왼쪽은 Java의 API, 오른쪽은 그에 대응하는 KSP 요소입니다.
출처: Java annotation processing to KSP reference
본문
프로그램 요소(Program elements)
| Java |
KSP에서 가장 가까운 기능 |
비고 |
AnnotationMirror |
KSAnnotation |
|
AnnotationValue |
KSValueArguments |
|
Element |
KSDeclaration/KSDeclarationContainer |
|
ExecutableElement |
KSFunctionDeclaration |
|
PackageElement |
KSFile |
KSP는 패키지를 프로그램 요소로 모델링하지 않아요 |
Parameterizable |
KSDeclaration |
|
QualifiedNameable |
KSDeclaration |
|
TypeElement |
KSClassDeclaration |
|
TypeParameterElement |
KSTypeParameter |
|
VariableElement |
KSValueParameter/KSPropertyDeclaration |
|
타입(Types)
KSP는 명시적인 타입 해석을 요구해요. 그래서 Java의 일부 기능들은 해석 전에는 KSType과 그에 대응하는 요소로만 구현할 수 있습니다.
| Java |
KSP에서 가장 가까운 기능 |
비고 |
ArrayType |
KSBuiltIns.arrayType |
|
DeclaredType |
KSType/KSClassifierReference |
|
ErrorType |
KSType.isError |
|
ExecutableType |
KSType/KSCallableReference |
|
IntersectionType |
KSType/KSTypeParameter |
|
NoType |
KSType.isError |
KSP에서는 해당 없음 |
NullType |
|
KSP에서는 해당 없음 |
PrimitiveType |
KSBuiltIns |
Java의 원시 타입과 정확히 같지는 않아요 |
ReferenceType |
KSTypeReference |
|
TypeMirror |
KSType |
|
TypeVariable |
KSTypeParameter |
|
UnionType |
해당 없음 |
Kotlin은 catch 블록마다 타입이 하나뿐이에요. UnionType은 Java 애너테이션 프로세서조차 관찰할 수 없어요 |
WildcardType |
KSType/KSTypeArgument |
|
기타(Misc)
| Java |
KSP에서 가장 가까운 기능 |
비고 |
Name |
KSName |
|
ElementKind |
ClassKind/FunctionKind |
|
Modifier |
Modifier |
|
NestingKind |
ClassKind/FunctionKind |
|
AnnotationValueVisitor |
|
|
ElementVisitor |
KSVisitor |
|
AnnotatedConstruct |
KSAnnotated |
|
TypeVisitor |
|
|
TypeKind |
KSBuiltIns |
일부는 builtins에서 찾고, 그 외에는 DeclaredType 확인을 위해 KSClassDeclaration을 살펴보세요 |
ElementFilter |
Collection.filterIsInstance |
|
ElementKindVisitor |
KSVisitor |
|
ElementScanner |
KSTopDownVisitor |
|
SimpleAnnotationValueVisitor |
|
KSP에서는 필요 없어요 |
SimpleElementVisitor |
KSVisitor |
|
SimpleTypeVisitor |
|
|
TypeKindVisitor |
|
|
Types |
Resolver/utils |
일부 utils 기능은 심볼 인터페이스에 통합되어 있어요 |
Elements |
Resolver/utils |
|
상세(Details)
Java 애너테이션 프로세싱 API의 기능을 KSP에서 어떻게 구현할 수 있는지 살펴볼게요.
AnnotationMirror
| Java |
KSP 대응 |
getAnnotationType |
ksAnnotation.annotationType |
getElementValues |
ksAnnotation.arguments |
AnnotationValue
| Java |
KSP 대응 |
getValue |
ksValueArgument.value |
Element
| Java |
KSP 대응 |
asType |
ksClassDeclaration.asType(...)은 KSClassDeclaration에서만 사용할 수 있어요. 타입 인자를 제공해야 합니다. |
getAnnotation |
구현 예정 |
getAnnotationMirrors |
ksDeclaration.annotations |
getEnclosedElements |
ksDeclarationContainer.declarations |
getEnclosingElements |
ksDeclaration.parentDeclaration |
getKind |
ClassKind 또는 FunctionKind에 따라 타입을 확인하고 캐스팅해요 |
getModifiers |
ksDeclaration.modifiers |
getSimpleName |
ksDeclaration.simpleName |
ExecutableElement
| Java |
KSP 대응 |
getDefaultValue |
구현 예정 |
getParameters |
ksFunctionDeclaration.parameters |
getReceiverType |
ksFunctionDeclaration.parentDeclaration |
getReturnType |
ksFunctionDeclaration.returnType |
getSimpleName |
ksFunctionDeclaration.simpleName |
getThrownTypes |
Kotlin에서는 필요 없어요 |
getTypeParameters |
ksFunctionDeclaration.typeParameters |
isDefault |
부모 선언이 인터페이스인지 확인해요 |
isVarArgs |
ksFunctionDeclaration.parameters.any { it.isVarArg } |
Parameterizable
| Java |
KSP 대응 |
getTypeParameters |
ksFunctionDeclaration.typeParameters |
QualifiedNameable
| Java |
KSP 대응 |
getQualifiedName |
ksDeclaration.qualifiedName |
TypeElement
| Java |
KSP 대응 |
getEnclosedElements |
ksClassDeclaration.declarations |
getEnclosingElement |
ksClassDeclaration.parentDeclaration |
getInterfaces |
```kotlin |
| // Should be able to do without resolution |
|
| ksClassDeclaration.superTypes |
|
.map { it.resolve() }
.filter { (it?.declaration as? KSClassDeclaration)?.classKind == ClassKind.INTERFACE }
| `getNestingKind` | `KSClassDeclaration.parentDeclaration`와 `inner` 수정자를 확인해요 |
| `getQualifiedName` | `ksClassDeclaration.qualifiedName` |
| `getSimpleName` | `ksClassDeclaration.simpleName` |
| `getSuperclass` | ```kotlin
// Should be able to do without resolution
ksClassDeclaration.superTypes
.map { it.resolve() }
.filter { (it?.declaration as? KSClassDeclaration)?.classKind == ClassKind.CLASS }
``` |
| `getTypeParameters` | `ksClassDeclaration.typeParameters` |
#### TypeParameterElement
| Java | KSP 대응 |
|---|---|
| `getBounds` | `ksTypeParameter.bounds` |
| `getEnclosingElement` | `ksTypeParameter.parentDeclaration` |
| `getGenericElement` | `ksTypeParameter.parentDeclaration` |
#### VariableElement
| Java | KSP 대응 |
|---|---|
| `getConstantValue` | 구현 예정 |
| `getEnclosingElement` | `ksValueParameter.parentDeclaration` |
| `getSimpleName` | `ksValueParameter.simpleName` |
#### ArrayType
| Java | KSP 대응 |
|---|---|
| `getComponentType` | `ksType.arguments.first()` |
#### DeclaredType
| Java | KSP 대응 |
|---|---|
| `asElement` | `ksType.declaration` |
| `getEnclosingType` | `ksType.declaration.parentDeclaration` |
| `getTypeArguments` | `ksType.arguments` |
#### ExecutableType
함수에 대한 `KSType`은 그저 `FunctionN<R, T1, T2, ..., TN>` 계열로 표현되는 시그니처예요.
| Java | KSP 대응 |
|---|---|
| `getParameterTypes` | `ksType.declaration.typeParameters`, `ksFunctionDeclaration.parameters.map { it.type }` |
| `getReceiverType` | `ksFunctionDeclaration.parentDeclaration.asType(...)` |
| `getReturnType` | `ksType.declaration.typeParameters.last()` |
| `getThrownTypes` | Kotlin에서는 필요 없어요 |
| `getTypeVariables` | `ksFunctionDeclaration.typeParameters` |
#### IntersectionType
| Java | KSP 대응 |
|---|---|
| `getBounds` | `ksTypeParameter.bounds` |
#### TypeMirror
| Java | KSP 대응 |
|---|---|
| `getKind` | 원시 타입과 `Unit` 타입은 `KSBuiltIns`의 타입과 비교하고, 그 외에는 declared 타입과 비교해요 |
#### TypeVariable
| Java | KSP 대응 |
|---|---|
| `asElement` | `ksType.declaration` |
| `getLowerBound` | 결정 예정. 캡처(capture)가 제공되고 명시적 경계 검사가 필요한 경우에만 필요해요. |
| `getUpperBound` | `ksTypeParameter.bounds` |
#### WildcardType
| **Java** | **KSP 대응** |
|---|---|
| `getExtendsBound` | ```kotlin
if (ksTypeArgument.variance == Variance.COVARIANT) ksTypeArgument.type else null
``` |
| `getSuperBound` | ```kotlin
if (ksTypeArgument.variance == Variance.CONTRAVARIANT) ksTypeArgument.type else null
``` |
#### Elements
| **Java** | **KSP 대응** |
|---|---|
| `getAllAnnotationMirrors` | `KSDeclarations.annotations` |
| `getAllMembers` | `getAllFunctions`, `getAllProperties`는 구현 예정이에요 |
| `getBinaryName` | 결정 예정, Java 사양 참조 |
| `getConstantExpression` | 상수 값은 있지만 표현식은 아니에요 |
| `getDocComment` | 구현 예정 |
| `getElementValuesWithDefaults` | 구현 예정 |
| `getName` | `resolver.getKSNameFromString` |
| `getPackageElement` | 패키지는 지원되지 않아요. 패키지 정보는 얻을 수 있지만 KSP로 패키지에 대한 연산은 불가능해요 |
| `getPackageOf` | 패키지는 지원되지 않아요 |
| `getTypeElement` | `Resolver.getClassDeclarationByName` |
| `hides` | 구현 예정 |
| `isDeprecated` | ```kotlin
KsDeclaration.annotations.any {
it.annotationType.resolve()!!.declaration.qualifiedName!!.asString() == Deprecated::class.qualifiedName
}
``` |
| `overrides` | `KSFunctionDeclaration.overrides`/`KSPropertyDeclaration.overrides`(각 클래스의 멤버 함수) |
| `printElements` | KSP는 대부분의 클래스에 기본 `toString()` 구현이 있어요 |
#### Types
| Java | KSP 대응 |
|---|---|
| `asElement` | `ksType.declaration` |
| `asMemberOf` | `resolver.asMemberOf` |
| `boxedClass` | 필요 없어요 |
| `capture` | 결정 예정 |
| `contains` | `KSType.isAssignableFrom` |
| `directSuperTypes` | `(ksType.declaration as KSClassDeclaration).superTypes` |
| `erasure` | `ksType.starProjection()` |
| `getArrayType` | `ksBuiltIns.arrayType.replace(...)` |
| `getDeclaredType` | `ksClassDeclaration.asType` |
| `getNoType` | `ksBuiltIns.nothingType`/`null` |
| `getNullType` | 상황에 따라 `KSType.markNullable`이 유용할 수 있어요 |
| `getPrimitiveType` | 필요 없음, `KSBuiltins`을 확인해요 |
| `getWildcardType` | `KSTypeArgument`가 기대되는 곳에서 `Variance`를 사용해요 |
| `isAssignable` | `ksType.isAssignableFrom` |
| `isSameType` | `ksType.equals` |
| `isSubsignature` | `functionTypeA == functionTypeB`/`functionTypeA == functionTypeB.starProjection()` |
| `isSubtype` | `ksType.isAssignableFrom` |
| `unboxedType` | 필요 없어요 |
## 더 알아보기
- [KSP 개요](https://kotlinlang.org/docs/ksp-overview.html)
- [KSP가 Kotlin 코드를 모델링하는 방식](https://kotlinlang.org/docs/ksp-why-are-generics-so-tricky.html)
- [KSP와 Kotlin Multiplatform](https://kotlinlang.org/docs/ksp-multiplatform.html)