Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
suspendfunfetchUserWithPosts(userId: String): UserProfile =
coroutineScope {
val user = async { userService.getUser(userId) }
val posts = async { postService.getUserPosts(userId) }
UserProfile(user = user.await(), posts = posts.await())
}
核心原则
1. 空安全
Kotlin 的类型系统区分可空和不可空类型。充分利用它。
// Good: Use non-nullable types by defaultfungetUser(id: String): User {
return userRepository.findById(id)
?: throw UserNotFoundException("User $id not found")
}
// Good: Safe calls and Elvis operatorfungetUserEmail(userId: String): String {
val user = userRepository.findById(userId)
return user?.email ?: "unknown@example.com"
}
// Bad: Force-unwrapping nullable typesfungetUserEmail(userId: String): String {
val user = userRepository.findById(userId)
return user!!.email // Throws NPE if null
}
2. 默认不可变性
优先使用 val 而非 var,优先使用不可变集合而非可变集合。
// Good: Immutable datadataclassUser(
val id: String,
val name: String,
val email: String,
)
// Good: Transform with copy()funupdateEmail(user: User, newEmail: String): User =
user.copy(email = newEmail)
// Good: Immutable collectionsval users: List<User> = listOf(user1, user2)
val filtered = users.filter { it.email.isNotBlank() }
// Bad: Mutable statevar currentUser: User? = null// Avoid mutable global stateval mutableUsers = mutableListOf<User>() // Avoid unless truly needed
3. 表达式体和单表达式函数
使用表达式体编写简洁、可读的函数。
// Good: Expression bodyfunisAdult(age: Int): Boolean = age >= 18funformatFullName(first: String, last: String): String =
"$first$last".trim()
fun User.displayName(): String =
name.ifBlank { email.substringBefore('@') }
// Good: When as expressionfunstatusMessage(code: Int): String = when (code) {
200 -> "OK"404 -> "Not Found"500 -> "Internal Server Error"else -> "Unknown status: $code"
}
// Bad: Unnecessary block bodyfunisAdult(age: Int): Boolean {
return age >= 18
}
4. 数据类用于值对象
使用数据类表示主要包含数据的类型。
// Good: Data class with copy, equals, hashCode, toStringdataclassCreateUserRequest(
val name: String,
val email: String,
val role: Role = Role.USER,
)
// Good: Value class for type safety (zero overhead at runtime)@JvmInline
value classUserId(val value: String) {
init {
require(value.isNotBlank()) { "UserId cannot be blank" }
}
}
@JvmInline
value classEmail(val value: String) {
init {
require('@'in value) { "Invalid email: $value" }
}
}
fungetUser(id: UserId): User = userRepository.findById(id)
密封类和接口
建模受限的层次结构
// Good: Sealed class for exhaustive whensealedclassResult<out T> {
dataclassSuccess<T>(valdata: T) : Result<T>()
dataclassFailure(val error: AppError) : Result<Nothing>()
dataobject Loading : Result<Nothing>()
}
fun<T> Result<T>.getOrNull(): T? = when (this) {
is Result.Success -> datais Result.Failure -> nullis Result.Loading -> null
}
fun<T> Result<T>.getOrThrow(): T = when (this) {
is Result.Success -> datais Result.Failure -> throw error.toException()
is Result.Loading -> throw IllegalStateException("Still loading")
}
用于 API 响应的密封接口
sealedinterfaceApiError {
val message: String
dataclassNotFound(overrideval message: String) : ApiError
dataclassUnauthorized(overrideval message: String) : ApiError
dataclassValidation(
overrideval message: String,
val field: String,
) : ApiError
dataclassInternal(
overrideval message: String,
val cause: Throwable? = null,
) : ApiError
}
fun ApiError.toStatusCode(): Int = when (this) {
is ApiError.NotFound -> 404is ApiError.Unauthorized -> 401is ApiError.Validation -> 422is ApiError.Internal -> 500
}
作用域函数
何时使用各个函数
// let: Transform nullable or scoped resultval length: Int? = name?.let { it.trim().length }
// apply: Configure an object (returns the object)val user = User().apply {
name = "Alice"
email = "alice@example.com"
}
// also: Side effects (returns the object)val user = createUser(request).also { logger.info("Created user: ${it.id}") }
// run: Execute a block with receiver (returns result)val result = connection.run {
prepareStatement(sql)
executeQuery()
}
// with: Non-extension form of runval csv = with(StringBuilder()) {
appendLine("name,email")
users.forEach { appendLine("${it.name},${it.email}") }
toString()
}
反模式
// Bad: Nesting scope functions
user?.let { u ->
u.address?.let { addr ->
addr.city?.let { city ->
println(city) // Hard to read
}
}
}
// Good: Chain safe calls insteadval city = user?.address?.city
city?.let { println(it) }
// Good: Respect cancellationsuspendfunprocessItems(items: List<Item>) {
items.forEach { item ->
ensureActive() // Check cancellation before expensive work
processItem(item)
}
}
// Good: Cleanup with try/finallysuspendfunacquireAndProcess() {
val resource = acquireResource()
try {
resource.process()
} finally {
withContext(NonCancellable) {
resource.release() // Always release, even on cancellation
}
}
}
委托
属性委托
// Lazy initializationval expensiveData: List<User> by lazy {
userRepository.findAll()
}
// Observable propertyvar name: String by Delegates.observable("initial") { _, old, new ->
logger.info("Name changed from '$old' to '$new'")
}
// Map-backed propertiesclassConfig(privateval map: Map<String, Any?>) {
val host: String by map
val port: Intby map
val debug: Booleanby map
}
val config = Config(mapOf("host" to "localhost", "port" to 8080, "debug" to true))
接口委托
// Good: Delegate interface implementationclassLoggingUserRepository(
privateval delegate: UserRepository,
privateval logger: Logger,
) : UserRepository by delegate {
// Only override what you need to add logging tooverridesuspendfunfindById(id: String): User? {
logger.info("Finding user by id: $id")
return delegate.findById(id).also {
logger.info("Found user: ${it?.name ?: "null"}")
}
}
}
DSL 构建器
类型安全构建器
// Good: DSL with @DslMarker@DslMarkerannotationclassHtmlDsl@HtmlDslclassHTML {
privateval children = mutableListOf<Element>()
funhead(init: Head.() -> Unit) {
children += Head().apply(init)
}
funbody(init: Body.() -> Unit) {
children += Body().apply(init)
}
overridefuntoString(): String = children.joinToString("\n")
}
funhtml(init: HTML.() -> Unit): HTML = HTML().apply(init)
// Usageval page = html {
head { title("My Page") }
body {
h1("Welcome")
p("Hello, World!")
}
}
配置 DSL
dataclassServerConfig(
val host: String = "0.0.0.0",
val port: Int = 8080,
val ssl: SslConfig? = null,
val database: DatabaseConfig? = null,
)
dataclassSslConfig(val certPath: String, val keyPath: String)
dataclassDatabaseConfig(val url: String, val maxPoolSize: Int = 10)
classServerConfigBuilder {
var host: String = "0.0.0.0"var port: Int = 8080privatevar ssl: SslConfig? = nullprivatevar database: DatabaseConfig? = nullfunssl(certPath: String, keyPath: String) {
ssl = SslConfig(certPath, keyPath)
}
fundatabase(url: String, maxPoolSize: Int = 10) {
database = DatabaseConfig(url, maxPoolSize)
}
funbuild(): ServerConfig = ServerConfig(host, port, ssl, database)
}
funserverConfig(init: ServerConfigBuilder.() -> Unit): ServerConfig =
ServerConfigBuilder().apply(init).build()
// Usageval config = serverConfig {
host = "0.0.0.0"
port = 443
ssl("/certs/cert.pem", "/certs/key.pem")
database("jdbc:postgresql://localhost:5432/mydb", maxPoolSize = 20)
}
用于惰性求值的序列
// Good: Use sequences for large collections with multiple operationsval result = users.asSequence()
.filter { it.isActive }
.map { it.email }
.filter { it.endsWith("@company.com") }
.take(10)
.toList()
// Good: Generate infinite sequencesval fibonacci: Sequence<Long> = sequence {
var a = 0Lvar b = 1Lwhile (true) {
yield(a)
val next = a + b
a = b
b = next
}
}
val first20 = fibonacci.take(20).toList()
Gradle Kotlin DSL
build.gradle.kts 配置
// Check for latest versions: https://kotlinlang.org/docs/releases.html
plugins {
kotlin("jvm") version "2.3.10"
kotlin("plugin.serialization") version "2.3.10"
id("io.ktor.plugin") version "3.4.0"
id("org.jetbrains.kotlinx.kover") version "0.9.7"
id("io.gitlab.arturbosch.detekt") version "1.23.8"
}
group = "com.example"
version = "1.0.0"
kotlin {
jvmToolchain(21)
}
dependencies {
// Ktor
implementation("io.ktor:ktor-server-core:3.4.0")
implementation("io.ktor:ktor-server-netty:3.4.0")
implementation("io.ktor:ktor-server-content-negotiation:3.4.0")
implementation("io.ktor:ktor-serialization-kotlinx-json:3.4.0")
// Exposed
implementation("org.jetbrains.exposed:exposed-core:1.0.0")
implementation("org.jetbrains.exposed:exposed-dao:1.0.0")
implementation("org.jetbrains.exposed:exposed-jdbc:1.0.0")
implementation("org.jetbrains.exposed:exposed-kotlin-datetime:1.0.0")
// Koin
implementation("io.insert-koin:koin-ktor:4.2.0")
// Coroutines
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
// Testing
testImplementation("io.kotest:kotest-runner-junit5:6.1.4")
testImplementation("io.kotest:kotest-assertions-core:6.1.4")
testImplementation("io.kotest:kotest-property:6.1.4")
testImplementation("io.mockk:mockk:1.14.9")
testImplementation("io.ktor:ktor-server-test-host:3.4.0")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.10.2")
}
tasks.withType<Test> {
useJUnitPlatform()
}
detekt {
config.setFrom(files("config/detekt/detekt.yml"))
buildUponDefaultConfig = true
}
错误处理模式
用于领域操作的 Result 类型
// Good: Use Kotlin's Result or a custom sealed classsuspendfuncreateUser(request: CreateUserRequest): Result<User> = runCatching {
require(request.name.isNotBlank()) { "Name cannot be blank" }
require('@'in request.email) { "Invalid email format" }
val user = User(
id = UserId(UUID.randomUUID().toString()),
name = request.name,
email = Email(request.email),
)
userRepository.save(user)
user
}
// Good: Chain resultsval displayName = createUser(request)
.map { it.name }
.getOrElse { "Unknown" }