<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="atom.xsl"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.cqkug.com/</id>
    <title>重庆 Kotlin User Group Blog</title>
    <updated>2026-10-17T02:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://www.cqkug.com/"/>
    <subtitle>重庆 Kotlin User Group Blog</subtitle>
    <icon>https://www.cqkug.com/img/favicon.ico</icon>
    <entry>
        <title type="html"><![CDATA[Kotlin 集合校验新姿势：告别 distinct().size 的低效写法]]></title>
        <id>https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis</id>
        <link href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis"/>
        <updated>2026-10-17T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[深入解析 Kotlin 2.4.20 新增的 allDistinct()、allEqual() 等集合校验函数，用语义化 API 替代 distinct().size == size 等低效模式]]></summary>
        <content type="html"><![CDATA[<p>在日常开发中，我们经常需要校验集合中元素的唯一性或一致性——比如检查用户提交的 ID 是否有重复、验证配置项是否全部相同、或者确保一批数据满足某种约束。在 Kotlin 2.4.20 之前，这些操作往往需要借助 <code>distinct().size</code> 或 <code>toSet().size</code> 这样的间接写法。Kotlin 2.4.20 带来了一组语义清晰的<strong>集合校验函数</strong>，让代码意图一目了然。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="旧写法能跑但不够好">旧写法：能跑，但不够好<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E6%97%A7%E5%86%99%E6%B3%95%E8%83%BD%E8%B7%91%E4%BD%86%E4%B8%8D%E5%A4%9F%E5%A5%BD" class="hash-link" aria-label="旧写法：能跑，但不够好的直接链接" title="旧写法：能跑，但不够好的直接链接" translate="no">​</a></h2>
<p>在 Kotlin 2.4.20 之前，检查集合元素是否全部唯一，最常见的写法是：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"user_001"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"user_002"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"user_003"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 检查是否有重复</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> allUnique </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">distinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 或者</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> noDuplicates </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toSet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><br></div></code></pre></div></div>
<p>这些写法能工作，但有几个问题：</p>
<ol>
<li class=""><strong>语义不直观</strong>：读者需要"翻译"一下才能理解意图——"去重后大小不变"等价于"没有重复"</li>
<li class=""><strong>性能浪费</strong>：<code>distinct()</code> 会创建一个全新的 List，<code>toSet()</code> 会创建一个 Set，仅仅是为了比较大小</li>
<li class=""><strong>无法按属性校验</strong>：如果要检查对象列表中某个属性是否唯一，代码更加冗长</li>
</ol>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> users </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Alice"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"2"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"bob@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Bob"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"charlie@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Charlie"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 检查邮箱是否唯一——旧写法</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> emailsUnique </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">email </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">distinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">email </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 这行代码太长了，而且 map 执行了两次</span><br></div></code></pre></div></div>
<p>检查所有元素是否相同也不优雅：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> statuses </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"active"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"active"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"active"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 检查是否全部相同——旧写法</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> allSame </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> statuses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toSet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 或者</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> allSame2 </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> statuses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">all</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> statuses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">first</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 空集合时 first() 会抛异常，需要额外处理</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="新-api四个函数覆盖全部场景">新 API：四个函数，覆盖全部场景<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E6%96%B0-api%E5%9B%9B%E4%B8%AA%E5%87%BD%E6%95%B0%E8%A6%86%E7%9B%96%E5%85%A8%E9%83%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="新 API：四个函数，覆盖全部场景的直接链接" title="新 API：四个函数，覆盖全部场景的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.4.20 引入了四个实验性函数：</p>
<table><thead><tr><th>函数</th><th>作用</th><th>返回</th></tr></thead><tbody><tr><td><code>allDistinct()</code></td><td>集合中所有元素是否互不相同</td><td><code>Boolean</code></td></tr><tr><td><code>allDistinctBy(selector)</code></td><td>按指定属性判断元素是否互不相同</td><td><code>Boolean</code></td></tr><tr><td><code>allEqual()</code></td><td>集合中所有元素是否完全相同</td><td><code>Boolean</code></td></tr><tr><td><code>allEqualBy(selector)</code></td><td>按指定属性判断元素是否完全相同</td><td><code>Boolean</code></td></tr></tbody></table>
<p>这些函数适用于 <code>Collection</code>、<code>Sequence</code> 和 <code>Array</code>，使用<strong>结构相等</strong>（<code>equals</code>）进行比较。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="alldistinct检查唯一性">allDistinct()：检查唯一性<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#alldistinct%E6%A3%80%E6%9F%A5%E5%94%AF%E4%B8%80%E6%80%A7" class="hash-link" aria-label="allDistinct()：检查唯一性的直接链接" title="allDistinct()：检查唯一性的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"user_001"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"user_002"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"user_003"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> withDuplicate </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"A"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"B"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"A"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">withDuplicate</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 空集合返回 true（vacuous truth）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">emptyList</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 单元素集合也返回 true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="alldistinctby按属性检查唯一性">allDistinctBy()：按属性检查唯一性<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#alldistinctby%E6%8C%89%E5%B1%9E%E6%80%A7%E6%A3%80%E6%9F%A5%E5%94%AF%E4%B8%80%E6%80%A7" class="hash-link" aria-label="allDistinctBy()：按属性检查唯一性的直接链接" title="allDistinctBy()：按属性检查唯一性的直接链接" translate="no">​</a></h3>
<p>这是最实用的函数之一——直接按对象的某个属性判断唯一性，无需先 <code>map</code> 再 <code>distinct</code>：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> users </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"1"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Alice"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"2"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"bob@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Bob"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"3"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"charlie@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Charlie"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 检查邮箱是否唯一</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinctBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">email </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 检查名字是否唯一</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">users</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinctBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 如果有重复邮箱</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> usersWithDup </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> users </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"4"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Alice2"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">usersWithDup</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinctBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">email </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="allequal-和-allequalby检查一致性">allEqual() 和 allEqualBy()：检查一致性<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#allequal-%E5%92%8C-allequalby%E6%A3%80%E6%9F%A5%E4%B8%80%E8%87%B4%E6%80%A7" class="hash-link" aria-label="allEqual() 和 allEqualBy()：检查一致性的直接链接" title="allEqual() 和 allEqualBy()：检查一致性的直接链接" translate="no">​</a></h3>
<p>当你需要验证一批数据是否"整齐划一"时，这两个函数就派上用场了：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 所有配置项相同</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> configs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"production"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"production"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"production"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">configs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqual</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> mixed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"dev"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"production"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"staging"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mixed</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqual</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 按属性检查一致性</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> status</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> tasks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"pending"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"pending"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">Task</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">3</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"pending"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tasks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqualBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">status </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// true</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能优势短路求值">性能优势：短路求值<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E6%80%A7%E8%83%BD%E4%BC%98%E5%8A%BF%E7%9F%AD%E8%B7%AF%E6%B1%82%E5%80%BC" class="hash-link" aria-label="性能优势：短路求值的直接链接" title="性能优势：短路求值的直接链接" translate="no">​</a></h2>
<p>这些新函数不仅仅是语法糖。与 <code>distinct().size == size</code> 不同，它们在发现第一个违规元素时就会<strong>立即返回</strong>，无需遍历整个集合：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 旧写法：必须遍历全部元素，创建完整 Set</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> hasNoDuplicates </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> largeList</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toSet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> largeList</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 新写法：遇到第一个重复就返回 false</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> hasNoDuplicates </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> largeList</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>对于大集合，尤其是重复元素出现在前部的场景，性能差异显著。<code>allEqual()</code> 同理——只要发现一个不同于首元素的值，就立即返回 <code>false</code>。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="实际应用场景">实际应用场景<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E5%AE%9E%E9%99%85%E5%BA%94%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="实际应用场景的直接链接" title="实际应用场景的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-数据校验">1. 数据校验<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#1-%E6%95%B0%E6%8D%AE%E6%A0%A1%E9%AA%8C" class="hash-link" aria-label="1. 数据校验的直接链接" title="1. 数据校验的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">validateOrder</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Order</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Result</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Order</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">order</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinctBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">productId </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">failure</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">IllegalArgumentException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"订单包含重复商品"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqualBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">quantity </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> order</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">items</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">first</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">quantity </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">failure</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">IllegalArgumentException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"所有商品数量为零"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Result</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">success</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">order</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-测试断言">2. 测试断言<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#2-%E6%B5%8B%E8%AF%95%E6%96%AD%E8%A8%80" class="hash-link" aria-label="2. 测试断言的直接链接" title="2. 测试断言的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@Test</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">`generated IDs should be unique`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> ids </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1</span><span class="token operator" style="color:#393A34">..</span><span class="token number" style="color:#36acaa">1000</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generateUniqueId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">assertTrue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ids</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"生成的 ID 存在重复"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@Test</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">`all tasks should have same priority after batch update`</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    service</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">batchUpdatePriority</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">taskIds</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Priority</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">HIGH</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> tasks </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> service</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getTasks</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">taskIds</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">assertTrue</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tasks</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqualBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">priority </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-问卷投票系统">3. 问卷/投票系统<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#3-%E9%97%AE%E5%8D%B7%E6%8A%95%E7%A5%A8%E7%B3%BB%E7%BB%9F" class="hash-link" aria-label="3. 问卷/投票系统的直接链接" title="3. 问卷/投票系统的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Response</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> participantId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> answer</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> responseDate</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">analyzeSurvey</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">responses</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> List</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Response</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 检查是否有重复提交</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">responses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinctBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">participantId </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"警告：存在同一参与者的多次提交"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 检查是否所有人答案一致</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">responses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqualBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">answer </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"所有参与者给出了相同答案"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 检查是否都在同一天提交</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">responses</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allEqualBy</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">responseDate </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"所有回答在同一天提交"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-配合-sequence-处理大数据流">4. 配合 Sequence 处理大数据流<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#4-%E9%85%8D%E5%90%88-sequence-%E5%A4%84%E7%90%86%E5%A4%A7%E6%95%B0%E6%8D%AE%E6%B5%81" class="hash-link" aria-label="4. 配合 Sequence 处理大数据流的直接链接" title="4. 配合 Sequence 处理大数据流的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">checkLargeDataset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// Sequence 的惰性求值 + allDistinct 的短路求值 = 高效组合</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> hasDuplicates </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">File</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"data.txt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">useLines</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> lines </span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            lines</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">filter</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> it</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">isNotEmpty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                 </span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">not</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何启用">如何启用<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E5%A6%82%E4%BD%95%E5%90%AF%E7%94%A8" class="hash-link" aria-label="如何启用的直接链接" title="如何启用的直接链接" translate="no">​</a></h2>
<p>这些函数目前是实验性的，需要添加 opt-in 注解：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 方式一：文件级注解</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@file:OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> com</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">example</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 方式二：函数级注解</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">myFunction</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> result </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> myList</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">allDistinct</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>或者在 <code>build.gradle.kts</code> 中全局启用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">kotlin </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compilerOptions </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        optIn</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"kotlin.ExperimentalStdlibApi"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="与相关-api-的对比">与相关 API 的对比<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E4%B8%8E%E7%9B%B8%E5%85%B3-api-%E7%9A%84%E5%AF%B9%E6%AF%94" class="hash-link" aria-label="与相关 API 的对比的直接链接" title="与相关 API 的对比的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>需求</th><th>旧写法</th><th>新写法</th></tr></thead><tbody><tr><td>元素是否唯一</td><td><code>list.distinct().size == list.size</code></td><td><code>list.allDistinct()</code></td></tr><tr><td>按属性唯一</td><td><code>list.map { it.id }.toSet().size == list.size</code></td><td><code>list.allDistinctBy { it.id }</code></td></tr><tr><td>元素是否相同</td><td><code>list.toSet().size == 1</code></td><td><code>list.allEqual()</code></td></tr><tr><td>按属性相同</td><td><code>list.map { it.status }.toSet().size == 1</code></td><td><code>list.allEqualBy { it.status }</code></td></tr><tr><td>获取重复元素</td><td><code>list.groupingBy { it }.eachCount().filter { it.value &gt; 1 }</code></td><td>仍需手动实现</td></tr></tbody></table>
<p>注意：新函数只返回 <code>Boolean</code>。如果你需要知道<strong>哪些元素重复了</strong>，仍然需要 <code>groupingBy</code> 等方案。但对于"是否合法"的校验场景，新 API 已经足够。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结">小结<a href="https://www.cqkug.com/2026/10/17/kotlin-collection-validation-apis#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.4.20 的集合校验函数看似简单，却解决了日常开发中一个高频痛点。它们用<strong>语义化的命名</strong>替代了需要"翻译"的间接写法，用<strong>短路求值</strong>替代了无意义的全量遍历，用<strong>一个函数调用</strong>替代了冗长的链式操作。</p>
<p>如果你的项目已经升级到 Kotlin 2.4.20+，不妨搜索一下代码中的 <code>distinct().size</code> 和 <code>toSet().size</code>，把它们替换成更优雅的新写法。</p>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="Kotlin" term="Kotlin"/>
        <category label="Kotlin 2.4" term="Kotlin 2.4"/>
        <category label="标准库" term="标准库"/>
        <category label="集合操作" term="集合操作"/>
        <category label="allDistinct" term="allDistinct"/>
        <category label="allEqual" term="allEqual"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kotlin 显式幕后字段实战：告别 _underscore 属性模式]]></title>
        <id>https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields</id>
        <link href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields"/>
        <updated>2026-10-16T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[深入解析 Kotlin 2.3.0 引入的显式幕后字段（Explicit Backing Fields），用 field 关键字消灭冗余的私有属性，让属性封装更简洁、更安全]]></summary>
        <content type="html"><![CDATA[<p>Kotlin 的属性系统一直是其核心优势之一——声明属性时，编译器自动生成 getter、setter 和幕后字段（backing field）。但在需要<strong>可变内部状态 + 只读外部接口</strong>的场景下，开发者被迫使用冗长的"双属性模式"。Kotlin 2.3.0 引入的 <strong>显式幕后字段（Explicit Backing Fields）</strong> 彻底改变了这一局面。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="痛点无处不在的双属性模式">痛点：无处不在的双属性模式<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E7%97%9B%E7%82%B9%E6%97%A0%E5%A4%84%E4%B8%8D%E5%9C%A8%E7%9A%84%E5%8F%8C%E5%B1%9E%E6%80%A7%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="痛点：无处不在的双属性模式的直接链接" title="痛点：无处不在的双属性模式的直接链接" translate="no">​</a></h2>
<p>如果你写过 Android 应用，这段代码一定不陌生：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> LocationViewModel </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> _city </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> city</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _city</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">updateCity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newCity</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        _city</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> newCity</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>为了对外暴露只读的 <code>StateFlow</code>，同时在内部保持可变性，我们不得不声明两个属性：一个私有的 <code>_city</code> 用于存储实际状态，一个公开的 <code>city</code> 通过 getter 暴露。这种 <strong>underscore 模式</strong> 在 Kotlin 项目中无处不在——搜索记录、缓存列表、用户偏好……每个需要"内变外不变"的场景都要写一遍。</p>
<p>更糟糕的是，当属性增多时，类中会充斥着大量 <code>_</code> 前缀的私有属性，代码可读性急剧下降：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> UserPreferences </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> _theme </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">LIGHT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> theme</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Theme</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _theme</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> _fontSize </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">14</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> fontSize</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Int</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _fontSize</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> _recentSearches </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> mutableListOf</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> recentSearches</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> List</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _recentSearches</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toList</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 还有更多...</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="新语法field-关键字">新语法：<code>field</code> 关键字<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E6%96%B0%E8%AF%AD%E6%B3%95field-%E5%85%B3%E9%94%AE%E5%AD%97" class="hash-link" aria-label="新语法field-关键字的直接链接" title="新语法field-关键字的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.3.0 允许你直接在属性声明中<strong>显式指定幕后字段</strong>，用一个属性替代原来的两个：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> LocationViewModel </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> city</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">""</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">updateCity</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newCity</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        city</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> newCity  </span><span class="token comment" style="color:#999988;font-style:italic">// 直接通过属性名访问幕后字段</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>这里的 <code>field = MutableStateFlow("")</code> 声明了一个<strong>真正存储在类中的字段</strong>，但它对外表现为 <code>val city: StateFlow&lt;String&gt;</code>——只读。类内部可以直接通过 <code>city</code> 访问和修改这个字段，外部调用者只能看到不可变的 <code>StateFlow</code>。</p>
<p>来看 <code>UserPreferences</code> 的简化版本：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> UserPreferences </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> theme</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Theme</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Theme</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">LIGHT</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> fontSize</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">Int</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">14</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> recentSearches</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> List</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">mutableListOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">addSearch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">query</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        recentSearches</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">query</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">recentSearches</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">size </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">10</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            recentSearches</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">removeAt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 使用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> prefs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">UserPreferences</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">prefs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">addSearch</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"Kotlin"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">prefs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">recentSearches</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// [Kotlin]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// prefs.recentSearches.add("Flow")  // 编译错误！外部无法修改</span><br></div></code></pre></div></div>
<p>三个属性，三种封装需求，代码量减少近一半。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="与自定义-gettersetter-配合">与自定义 getter/setter 配合<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E4%B8%8E%E8%87%AA%E5%AE%9A%E4%B9%89-gettersetter-%E9%85%8D%E5%90%88" class="hash-link" aria-label="与自定义 getter/setter 配合的直接链接" title="与自定义 getter/setter 配合的直接链接" translate="no">​</a></h2>
<p>显式幕后字段最强大的地方在于可以和自定义访问器无缝配合。考虑一个带验证逻辑的属性：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> UserProfile </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">trim</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> age</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">coerceAtLeast</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>这里的 <code>field</code> 是传统的隐式幕后字段引用（Kotlin 1.0 就有的特性）。而<strong>显式</strong>幕后字段的不同之处在于，你可以<strong>指定字段的类型和初始值</strong>，甚至让字段类型与属性声明类型不同：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> CachedApiClient </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 属性对外是 String?，幕后字段是 MutableStateFlow&lt;String?&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 带自定义 getter 的缓存属性</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> cachedData</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Map</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Any</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">emptyMap</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">field</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">isEmpty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">loadFromDisk</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> field</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">set</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="smart-cast-自动生效">Smart Cast 自动生效<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#smart-cast-%E8%87%AA%E5%8A%A8%E7%94%9F%E6%95%88" class="hash-link" aria-label="Smart Cast 自动生效的直接链接" title="Smart Cast 自动生效的直接链接" translate="no">​</a></h2>
<p>显式幕后字段的一个隐藏优势是 <strong>Smart Cast 可以正常工作</strong>。在旧的双属性模式中，这是一个常见的痛点：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 旧模式：Smart Cast 经常失败</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> OldViewModel </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">private</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> _user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> MutableStateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">User</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> user</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">User</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> _user</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">greet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vm</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> OldViewModel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> u </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> vm</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">user</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">u </span><span class="token operator" style="color:#393A34">!=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">u</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// OK，但需要局部变量</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// vm.user.value.name  // 不行！Smart Cast 无法跨越 getter 调用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 新模式：Smart Cast 直接可用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> NewViewModel </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> user</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StateFlow</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">User</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MutableStateFlow</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">null</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">greet</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">vm</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> NewViewModel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 在类内部直接访问 field 时，Smart Cast 正常工作</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何启用">如何启用<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E5%A6%82%E4%BD%95%E5%90%AF%E7%94%A8" class="hash-link" aria-label="如何启用的直接链接" title="如何启用的直接链接" translate="no">​</a></h2>
<p>显式幕后字段在 Kotlin 2.3.0 中作为实验性特性引入，需要添加编译器参数：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// build.gradle.kts</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">kotlin </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compilerOptions </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        freeCompilerArgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"-Xexplicit-backing-fields"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">&lt;!-- pom.xml (Maven) --&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">plugin</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">groupId</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">org.jetbrains.kotlin</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">groupId</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">artifactId</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">kotlin-maven-plugin</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">artifactId</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">args</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token tag punctuation" style="color:#393A34">&lt;</span><span class="token tag" style="color:#00009f">arg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain">-Xexplicit-backing-fields</span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">arg</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">args</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">configuration</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token tag punctuation" style="color:#393A34">&lt;/</span><span class="token tag" style="color:#00009f">plugin</span><span class="token tag punctuation" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<p>在 KotlinConf'26 主题演讲中，JetBrains 宣布该特性正在走向稳定。如果你使用的是 Kotlin 2.4.x，可以关注后续版本中移除实验性标记的进展。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="不适用场景">不适用场景<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E4%B8%8D%E9%80%82%E7%94%A8%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="不适用场景的直接链接" title="不适用场景的直接链接" translate="no">​</a></h2>
<p>显式幕后字段并非万能。以下场景<strong>不能</strong>使用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// ❌ val 属性没有 setter，field 不可变</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> computed</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 无法在后续修改</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ❌ lateinit 属性不支持</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">lateinit</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> lazy</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">""</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 编译错误</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ❌ 委托属性不支持</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">var</span><span class="token plain"> delegated</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String </span><span class="token keyword" style="color:#00009f">by</span><span class="token plain"> lazy </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"hello"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">""</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 编译错误，field 被委托对象接管</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ❌ 接口属性没有幕后字段</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">interface</span><span class="token plain"> Config </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Long</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        field </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000L</span><span class="token plain">  </span><span class="token comment" style="color:#999988;font-style:italic">// 编译错误</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>记住一条原则：<strong>只有带自定义 getter 或 setter 的 <code>var</code> 属性</strong>才能使用显式幕后字段。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能零开销">性能零开销<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E6%80%A7%E8%83%BD%E9%9B%B6%E5%BC%80%E9%94%80" class="hash-link" aria-label="性能零开销的直接链接" title="性能零开销的直接链接" translate="no">​</a></h2>
<p>通过 <code>Tools → Kotlin → Show Kotlin Bytecode → Decompile</code> 查看字节码，显式幕后字段编译后与手写双属性模式<strong>完全一致</strong>——都是 <code>private</code> 字段加 getter。没有反射，没有额外对象分配，运行时开销为零。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结">小结<a href="https://www.cqkug.com/2026/10/16/kotlin-explicit-backing-fields#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接" translate="no">​</a></h2>
<p>显式幕后字段解决了 Kotlin 中一个长期存在的样板代码问题。它不是新概念的发明，而是对已有 <code>field</code> 关键字的自然延伸——从"隐式引用"到"显式声明"，让属性的封装意图更加清晰。对于大量使用 <code>StateFlow</code>、缓存、惰性加载的 Kotlin 项目，这是一个立竿见影的代码简化工具。</p>
<p>你的项目中有多少对 <code>_xxx</code> / <code>xxx</code> 属性？是时候给它们"瘦身"了。</p>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="Kotlin" term="Kotlin"/>
        <category label="Kotlin 2.3" term="Kotlin 2.3"/>
        <category label="显式幕后字段" term="显式幕后字段"/>
        <category label="StateFlow" term="StateFlow"/>
        <category label="属性封装" term="属性封装"/>
        <category label="backing field" term="backing field"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kotlin 命名解构实战：告别 componentN() 的顺序陷阱]]></title>
        <id>https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring</id>
        <link href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring"/>
        <updated>2026-10-15T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[深入解析 Kotlin 2.3.20 引入的基于名称的解构声明，让 data class 解构不再因属性顺序而出错]]></summary>
        <content type="html"><![CDATA[<p>Kotlin 的解构声明（destructuring declaration）一直是开发者喜爱的语法糖——一行代码就能从 data class 中提取多个属性。但它有一个隐藏的陷阱：<strong>完全依赖属性顺序</strong>。Kotlin 2.3.20 引入了 <strong>基于名称的解构（Name-based Destructuring）</strong>，从根本上解决了这个问题。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="痛点一个沉默的-bug">痛点：一个沉默的 Bug<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E7%97%9B%E7%82%B9%E4%B8%80%E4%B8%AA%E6%B2%89%E9%BB%98%E7%9A%84-bug" class="hash-link" aria-label="痛点：一个沉默的 Bug的直接链接" title="痛点：一个沉默的 Bug的直接链接" translate="no">​</a></h2>
<p>先看一段看似无害的代码：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">processUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">user</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> User</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"用户 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">username</span><span class="token string-literal singleline string" style="color:#e3116c"> 的邮箱是 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">email</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">processUser</span><span class="token punctuation" style="color:#393A34">(</span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"alice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 输出：用户 alice 的邮箱是 alice@example.com</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 等等……这不对！</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>发现问题了吗？<code>val (email, username) = user</code> 中，<code>email</code> 实际拿到的是 <code>username</code> 的值（<code>"alice"</code>），而 <code>username</code> 拿到的是 <code>email</code> 的值（<code>"alice@example.com"</code>）。</p>
<p>因为两个属性都是 <code>String</code> 类型，<strong>编译器不会报任何错误</strong>。这个 Bug 在生产环境中可能悄悄存在很久。</p>
<p>更可怕的是，如果同事重构了 <code>User</code> 类，调整了属性顺序：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 重构前</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 重构后</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>项目中所有使用位置解构的代码都会<strong>静默地交换值</strong>，没有任何编译警告。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="新语法按名称解构">新语法：按名称解构<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E6%96%B0%E8%AF%AD%E6%B3%95%E6%8C%89%E5%90%8D%E7%A7%B0%E8%A7%A3%E6%9E%84" class="hash-link" aria-label="新语法：按名称解构的直接链接" title="新语法：按名称解构的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.3.20 引入了基于名称的解构声明。变量不再按 <code>component1()</code>、<code>component2()</code> 的位置绑定，而是<strong>按属性名匹配</strong>：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"alice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 显式形式：val 变量名 = 属性名</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> mail </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// alice</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">mail</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// alice@example.com</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>注意语法要点：</p>
<ul>
<li class="">使用<strong>圆括号</strong>包裹，每个变量前需要写 <code>val</code></li>
<li class="">等号左边是<strong>局部变量名</strong>，右边是<strong>属性名</strong></li>
<li class=""><strong>顺序无关</strong>——<code>(val name = username, val mail = email)</code> 效果完全相同</li>
<li class="">可以<strong>重命名</strong>——属性 <code>email</code> 提取后叫 <code>mail</code></li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="三种编译器模式">三种编译器模式<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E4%B8%89%E7%A7%8D%E7%BC%96%E8%AF%91%E5%99%A8%E6%A8%A1%E5%BC%8F" class="hash-link" aria-label="三种编译器模式的直接链接" title="三种编译器模式的直接链接" translate="no">​</a></h2>
<p>这个特性通过 <code>-Xname-based-destructuring</code> 编译器参数控制，支持三种模式：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-only-syntax推荐入门">1. <code>only-syntax</code>（推荐入门）<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#1-only-syntax%E6%8E%A8%E8%8D%90%E5%85%A5%E9%97%A8" class="hash-link" aria-label="1-only-syntax推荐入门的直接链接" title="1-only-syntax推荐入门的直接链接" translate="no">​</a></h3>
<p>只启用显式的按名称解构语法，<strong>不改变</strong>现有圆括号解构的行为：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// build.gradle.kts</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">kotlin </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compilerOptions </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        freeCompilerArgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"-Xname-based-destructuring=only-syntax"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> user </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">User</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"alice"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"alice@example.com"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 传统写法依然按位置（向后兼容）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">first</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> second</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user  </span><span class="token comment" style="color:#999988;font-style:italic">// first = username, second = email</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 新写法按名称</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> mail </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-name-mismatch渐进迁移">2. <code>name-mismatch</code>（渐进迁移）<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#2-name-mismatch%E6%B8%90%E8%BF%9B%E8%BF%81%E7%A7%BB" class="hash-link" aria-label="2-name-mismatch渐进迁移的直接链接" title="2-name-mismatch渐进迁移的直接链接" translate="no">​</a></h3>
<p>在 <code>only-syntax</code> 的基础上，如果位置解构时变量名和属性名对不上，编译器会给出 <strong>warning</strong>：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Warning: Variable 'email' name does not match property name 'username'</span><br></div></code></pre></div></div>
<p>这非常适合在现有项目中做渐进式迁移——先开启 warning，逐步修复不匹配的解构，再考虑切换到更激进的模式。</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-complete面向未来">3. <code>complete</code>（面向未来）<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#3-complete%E9%9D%A2%E5%90%91%E6%9C%AA%E6%9D%A5" class="hash-link" aria-label="3-complete面向未来的直接链接" title="3-complete面向未来的直接链接" translate="no">​</a></h3>
<p>连传统的 <code>val (a, b)</code> 也会<strong>按名称匹配</strong>，而按位置解构改用<strong>方括号</strong>语法：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 按名称（默认行为变了！）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user  </span><span class="token comment" style="color:#999988;font-style:italic">// 自动按名称匹配</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 按位置（新语法）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">first</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> second</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user    </span><span class="token comment" style="color:#999988;font-style:italic">// 按 componentN() 顺序</span><br></div></code></pre></div></div>
<p>这个模式最具颠覆性，适合新项目或已完成迁移的项目。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="在循环和-lambda-中使用">在循环和 Lambda 中使用<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E5%9C%A8%E5%BE%AA%E7%8E%AF%E5%92%8C-lambda-%E4%B8%AD%E4%BD%BF%E7%94%A8" class="hash-link" aria-label="在循环和 Lambda 中使用的直接链接" title="在循环和 Lambda 中使用的直接链接" translate="no">​</a></h2>
<p>命名解构不仅适用于普通的 <code>val</code> 声明，还能在 <code>for</code> 循环和 Lambda 参数中使用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Person</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> surname</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> age</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> people </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">listOf</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">Person</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"John"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Doe"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">30</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">Person</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"Jane"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"Bar"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">25</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// for 循环中按名称解构</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> surname</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">in</span><span class="token plain"> people</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"Surname: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">surname</span><span class="token string-literal singleline string" style="color:#e3116c">, Name: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">name</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Lambda 参数中按名称解构</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">people</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">forEach</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> name</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> surname</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">-&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"Name: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">name</span><span class="token string-literal singleline string" style="color:#e3116c">, Surname: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">surname</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="不再需要-operator-函数">不再需要 operator 函数<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E4%B8%8D%E5%86%8D%E9%9C%80%E8%A6%81-operator-%E5%87%BD%E6%95%B0" class="hash-link" aria-label="不再需要 operator 函数的直接链接" title="不再需要 operator 函数的直接链接" translate="no">​</a></h2>
<p>传统的解构依赖 <code>component1()</code>、<code>component2()</code> 等 operator 函数，而命名解构<strong>直接按属性名匹配</strong>，这意味着普通类（非 data class）也能享受解构的便利：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> host</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> port</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> timeout</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Long</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> config </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Config</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"localhost"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8080</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">3000L</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 不需要 componentN()，直接按属性名解构</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> server </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> host</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> p </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> port</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> config</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"连接 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">server</span><span class="token string-literal singleline string" style="color:#e3116c">:</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">p</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="迁移建议">迁移建议<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E8%BF%81%E7%A7%BB%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="迁移建议的直接链接" title="迁移建议的直接链接" translate="no">​</a></h2>
<p>对于已有项目，建议分三步走：</p>
<ol>
<li class=""><strong>第一步</strong>：开启 <code>only-syntax</code> 模式，新代码使用显式命名解构</li>
<li class=""><strong>第二步</strong>：切换到 <code>name-mismatch</code>，利用 warning 定位现有代码中的潜在问题</li>
<li class=""><strong>第三步</strong>：评估是否迁移到 <code>complete</code> 模式（需同时引入方括号按位置解构）</li>
</ol>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 迁移示例</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// Before（容易出错）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// After（安全、清晰）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> email</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> username</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> user  </span><span class="token comment" style="color:#999988;font-style:italic">// 显式命名解构</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="小结">小结<a href="https://www.cqkug.com/2026/10/15/kotlin-name-based-destructuring#%E5%B0%8F%E7%BB%93" class="hash-link" aria-label="小结的直接链接" title="小结的直接链接" translate="no">​</a></h2>
<p>Kotlin 的命名解构是一个迟到但实用的特性。它解决了长期以来 data class 解构中"顺序即正确"的脆弱性，让重构更安全，让代码意图更清晰。虽然目前仍是实验性特性，但已经值得在新代码中尝试。</p>
<p>你遇到过因解构顺序导致的 Bug 吗？欢迎在评论区分享你的经历。</p>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="Kotlin" term="Kotlin"/>
        <category label="Kotlin 2.3" term="Kotlin 2.3"/>
        <category label="解构声明" term="解构声明"/>
        <category label="data class" term="data class"/>
        <category label="语言特性" term="语言特性"/>
        <category label="name-based destructuring" term="name-based destructuring"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kotlin 2.4.20 协程调试新体验：堆栈跟踪恢复实战]]></title>
        <id>https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery</id>
        <link href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery"/>
        <updated>2026-10-14T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[深入解析 Kotlin 2.4.20 引入的协程堆栈跟踪恢复机制，让异步代码调试不再痛苦]]></summary>
        <content type="html"><![CDATA[<p>协程是 Kotlin 最强大的特性之一，但调试异步代码一直是开发者的痛点。当异常在协程中抛出时，堆栈跟踪往往会"跳跃"——你看到的调用栈可能完全不包含实际出错的代码位置。Kotlin 2.4.20 引入了全新的 <strong>堆栈跟踪恢复（Stack Trace Recovery）</strong> 机制，彻底改善了这一状况。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="痛点协程异常堆栈为何支离破碎">痛点：协程异常堆栈为何"支离破碎"？<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E7%97%9B%E7%82%B9%E5%8D%8F%E7%A8%8B%E5%BC%82%E5%B8%B8%E5%A0%86%E6%A0%88%E4%B8%BA%E4%BD%95%E6%94%AF%E7%A6%BB%E7%A0%B4%E7%A2%8E" class="hash-link" aria-label="痛点：协程异常堆栈为何&quot;支离破碎&quot;？的直接链接" title="痛点：协程异常堆栈为何&quot;支离破碎&quot;？的直接链接" translate="no">​</a></h2>
<p>在 <code>kotlinx.coroutines</code> 中，当异常跨越协程边界时，框架会创建新的异常实例来保留原始上下文。这个过程中，自定义异常的属性（如错误码、行号等）往往会丢失：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 旧行为：自定义属性在堆栈恢复时丢失</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">FileProcessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> lineNumber</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> filePath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cause</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Throwable</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RuntimeException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"处理文件 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">filePath</span><span class="token string-literal singleline string" style="color:#e3116c"> 第 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">lineNumber</span><span class="token string-literal singleline string" style="color:#e3116c"> 行时出错"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cause</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">suspend</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">processFile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">path</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 模拟在第 42 行出错</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">FileProcessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">42</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> path</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">NumberFormatException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"无效的数字格式"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">suspend</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        coroutineScope </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            async </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">processFile</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"/data/config.txt"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> FileProcessException</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 问题：e.lineNumber 可能变成默认值 0</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// e.filePath 可能变成 null</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"出错行号: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">lineNumber</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 输出 0，而不是 42！</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>这是因为 <code>kotlinx.coroutines</code> 在恢复堆栈时，通过反射创建新的异常实例，但无法正确传递自定义构造参数。结果就是：你看到了堆栈，但丢失了关键的业务上下文。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="解决方案stacktracerecoverable-接口">解决方案：StackTraceRecoverable 接口<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E8%A7%A3%E5%86%B3%E6%96%B9%E6%A1%88stacktracerecoverable-%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="解决方案：StackTraceRecoverable 接口的直接链接" title="解决方案：StackTraceRecoverable 接口的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.4.20 在标准库中引入了 <code>StackTraceRecoverable</code> 接口，让自定义异常能够控制如何在堆栈恢复时复制自身：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibCoroutineSupportApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">FileProcessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> lineNumber</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> filePath</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cause</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Throwable</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RuntimeException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"处理文件 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">filePath</span><span class="token string-literal singleline string" style="color:#e3116c"> 第 </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">lineNumber</span><span class="token string-literal singleline string" style="color:#e3116c"> 行时出错"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cause</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    StackTraceRecoverable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">FileProcessException</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token comment" style="color:#999988;font-style:italic">// 关键：告诉框架如何正确复制这个异常</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> FileProcessException </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">FileProcessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">lineNumber</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> filePath</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>实现这个接口后，当 <code>kotlinx.coroutines</code> 需要创建新的异常实例时，会调用 <code>copyForStackTraceRecovery()</code> 方法，确保所有自定义属性都被正确保留。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="实战完整的调试场景">实战：完整的调试场景<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E5%AE%9E%E6%88%98%E5%AE%8C%E6%95%B4%E7%9A%84%E8%B0%83%E8%AF%95%E5%9C%BA%E6%99%AF" class="hash-link" aria-label="实战：完整的调试场景的直接链接" title="实战：完整的调试场景的直接链接" translate="no">​</a></h2>
<p>让我们看一个更完整的实际场景：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibCoroutineSupportApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">data</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ApiErrorContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> endpoint</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> userId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> requestId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> timestamp</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Long </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> System</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">currentTimeMillis</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibCoroutineSupportApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ApiException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> statusCode</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Int</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> context</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ApiErrorContext</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cause</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Throwable</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RuntimeException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-literal singleline string" style="color:#e3116c">"API 请求失败 [</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">endpoint</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">] 状态码: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">statusCode</span><span class="token string-literal singleline string" style="color:#e3116c">, "</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token string-literal singleline string" style="color:#e3116c">"用户: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">userId</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">, 请求ID: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">requestId</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cause</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> StackTraceRecoverable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">ApiException</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ApiException </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">ApiException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">statusCode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 使用场景</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">suspend</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fetchUserData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userId</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> UserData </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> requestId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">generateRequestId</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> context </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ApiErrorContext</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        endpoint </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"/api/users/</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">$</span><span class="token string-literal singleline interpolation expression">userId</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        userId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> userId</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        requestId </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> requestId</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        httpClient</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">context</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">endpoint</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">header</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"X-Request-ID"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> requestId</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">body</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ClientRequestException</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">throw</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">ApiException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">response</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">status</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> context</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">suspend</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    coroutineScope </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> deferred </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> async </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">fetchUserData</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"user_12345"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            deferred</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">await</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> ApiException</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// 现在所有属性都完整保留！</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"错误上下文:"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"  端点: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">endpoint</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"  用户: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">userId</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"  请求ID: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">requestId</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"  时间戳: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">context</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">timestamp</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"  状态码: </span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">${</span><span class="token string-literal singleline interpolation expression">e</span><span class="token string-literal singleline interpolation expression punctuation" style="color:#393A34">.</span><span class="token string-literal singleline interpolation expression">statusCode</span><span class="token string-literal singleline interpolation interpolation-punctuation punctuation" style="color:#393A34">}</span><span class="token string-literal singleline string" style="color:#e3116c">"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token comment" style="color:#999988;font-style:italic">// 堆栈跟踪也完整保留了原始位置</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            e</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">printStackTrace</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="启用方式">启用方式<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E5%90%AF%E7%94%A8%E6%96%B9%E5%BC%8F" class="hash-link" aria-label="启用方式的直接链接" title="启用方式的直接链接" translate="no">​</a></h2>
<p>这个特性目前是实验性的，需要两个步骤来启用：</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-编译器配置">1. 编译器配置<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#1-%E7%BC%96%E8%AF%91%E5%99%A8%E9%85%8D%E7%BD%AE" class="hash-link" aria-label="1. 编译器配置的直接链接" title="1. 编译器配置的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// build.gradle.kts</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">kotlin </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compilerOptions </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        freeCompilerArgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"-Xstack-trace-recovery"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-代码中-opt-in">2. 代码中 opt-in<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#2-%E4%BB%A3%E7%A0%81%E4%B8%AD-opt-in" class="hash-link" aria-label="2. 代码中 opt-in的直接链接" title="2. 代码中 opt-in的直接链接" translate="no">​</a></h3>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibCoroutineSupportApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MyCustomException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">..</span><span class="token punctuation" style="color:#393A34">.</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> StackTraceRecoverable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">MyCustomException</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> MyCustomException </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">..</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-依赖版本要求">3. 依赖版本要求<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#3-%E4%BE%9D%E8%B5%96%E7%89%88%E6%9C%AC%E8%A6%81%E6%B1%82" class="hash-link" aria-label="3. 依赖版本要求的直接链接" title="3. 依赖版本要求的直接链接" translate="no">​</a></h3>
<p>确保使用兼容的版本：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">dependencies </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">implementation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"org.jetbrains.kotlin:kotlin-stdlib:2.4.20"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">implementation</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0+"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="最佳实践">最佳实践<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5" class="hash-link" aria-label="最佳实践的直接链接" title="最佳实践的直接链接" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-优先为业务异常实现接口">1. 优先为业务异常实现接口<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#1-%E4%BC%98%E5%85%88%E4%B8%BA%E4%B8%9A%E5%8A%A1%E5%BC%82%E5%B8%B8%E5%AE%9E%E7%8E%B0%E6%8E%A5%E5%8F%A3" class="hash-link" aria-label="1. 优先为业务异常实现接口的直接链接" title="1. 优先为业务异常实现接口的直接链接" translate="no">​</a></h3>
<p>如果你的项目中有自定义异常类，并且会在协程中抛出，建议都实现 <code>StackTraceRecoverable</code>：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 通用模板</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token annotation builtin">@OptIn</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">ExperimentalStdlibCoroutineSupportApi</span><span class="token operator" style="color:#393A34">::</span><span class="token keyword" style="color:#00009f">class</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">BusinessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> errorCode</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> userMessage</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> details</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Map</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">String</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Any</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    cause</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Throwable</span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">null</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">RuntimeException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">userMessage</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cause</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    StackTraceRecoverable</span><span class="token operator" style="color:#393A34">&lt;</span><span class="token plain">BusinessException</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token function" style="color:#d73a49">BusinessException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">errorCode</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> userMessage</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> details</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-保持复制逻辑简单">2. 保持复制逻辑简单<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#2-%E4%BF%9D%E6%8C%81%E5%A4%8D%E5%88%B6%E9%80%BB%E8%BE%91%E7%AE%80%E5%8D%95" class="hash-link" aria-label="2. 保持复制逻辑简单的直接链接" title="2. 保持复制逻辑简单的直接链接" translate="no">​</a></h3>
<p><code>copyForStackTraceRecovery()</code> 应该只做简单的属性复制，不要包含复杂的业务逻辑：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// ✅ 好的做法：简单复制</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">MyException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">field1</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> field2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// ❌ 避免：复杂逻辑</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> MyException </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> processed </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">complexProcessing</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">field1</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 不要这样做</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">MyException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">processed</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> field2</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-正确处理-cause-链">3. 正确处理 cause 链<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#3-%E6%AD%A3%E7%A1%AE%E5%A4%84%E7%90%86-cause-%E9%93%BE" class="hash-link" aria-label="3. 正确处理 cause 链的直接链接" title="3. 正确处理 cause 链的直接链接" translate="no">​</a></h3>
<p>复制时应该将当前实例作为新实例的 cause，这样能保留完整的异常链：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">override</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">copyForStackTraceRecovery</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">MyException</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">importantData</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// this 作为 cause</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="性能影响">性能影响<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E6%80%A7%E8%83%BD%E5%BD%B1%E5%93%8D" class="hash-link" aria-label="性能影响的直接链接" title="性能影响的直接链接" translate="no">​</a></h2>
<p>堆栈跟踪恢复会带来一定的性能开销，但只在异常发生时才会触发。在正常执行路径上，几乎没有额外成本：</p>
<ul>
<li class=""><strong>正常执行</strong>：零开销</li>
<li class=""><strong>异常发生时</strong>：额外的对象创建和属性复制</li>
<li class=""><strong>建议</strong>：在性能敏感的代码路径上，可以通过基准测试验证影响</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="与其他调试工具配合">与其他调试工具配合<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E4%B8%8E%E5%85%B6%E4%BB%96%E8%B0%83%E8%AF%95%E5%B7%A5%E5%85%B7%E9%85%8D%E5%90%88" class="hash-link" aria-label="与其他调试工具配合的直接链接" title="与其他调试工具配合的直接链接" translate="no">​</a></h2>
<p>堆栈跟踪恢复可以与 Kotlin 的协程调试工具配合使用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 启用协程调试模式</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">System</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setProperty</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"kotlinx.coroutines.debug"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"on"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 在异常日志中同时输出：</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 1. 完整的堆栈跟踪（包含协程名称）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 2. 自定义的业务上下文</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// 3. 协程的创建和恢复位置</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://www.cqkug.com/2026/10/14/kotlin-coroutine-stack-trace-recovery#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.4.20 的堆栈跟踪恢复机制解决了协程调试中的一个长期痛点。通过实现简单的 <code>StackTraceRecoverable</code> 接口，你的自定义异常就能在异步边界保持完整的上下文信息。</p>
<p><strong>关键要点：</strong></p>
<ol>
<li class="">协程异常堆栈恢复会导致自定义属性丢失</li>
<li class="">实现 <code>StackTraceRecoverable</code> 接口可以解决这个问题</li>
<li class="">复制方法应该简单直接，保持 cause 链完整</li>
<li class="">建议所有在协程中使用的自定义异常都实现这个接口</li>
</ol>
<p>虽然这个特性目前还是实验性的，但它的设计已经相当成熟。如果你的项目重度依赖协程，现在就可以开始试用，让异步代码的调试变得更加轻松。</p>
<blockquote>
<p>参考资料：</p>
<ul>
<li class=""><a href="https://kotlinlang.org/docs/whatsnew2420.html" target="_blank" rel="noopener noreferrer" class="">What's new in Kotlin 2.4.20</a></li>
<li class=""><a href="https://kotlinlang.org/docs/coroutines-guide.html" target="_blank" rel="noopener noreferrer" class="">Kotlin Coroutines Documentation</a></li>
<li class=""><a href="https://github.com/Kotlin/KEEP/blob/master/proposals/stdlib/coroutine-stack-trace-recovery.md" target="_blank" rel="noopener noreferrer" class="">KEEP: Coroutine Stack Trace Recovery</a></li>
</ul>
</blockquote>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="Kotlin" term="Kotlin"/>
        <category label="Kotlin 2.4" term="Kotlin 2.4"/>
        <category label="协程" term="协程"/>
        <category label="调试" term="调试"/>
        <category label="堆栈跟踪" term="堆栈跟踪"/>
        <category label="coroutines" term="coroutines"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Kotlin 2.5 新特性：伴生块与伴生扩展实战指南]]></title>
        <id>https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions</id>
        <link href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions"/>
        <updated>2026-10-13T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[深入解析 Kotlin 2.5.0 引入的实验性特性——伴生块（companion blocks）与伴生扩展（companion extensions），告别 companion object 的繁琐写法]]></summary>
        <content type="html"><![CDATA[<p>Kotlin 2.5.0 引入了一个令人兴奋的实验性特性：<strong>伴生块（companion blocks）</strong> 与 <strong>伴生扩展（companion extensions）</strong>。它们从根本上改变了我们定义类级别成员的方式，让代码更简洁，互操作性更强。</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="为什么需要新特性">为什么需要新特性？<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E4%B8%BA%E4%BB%80%E4%B9%88%E9%9C%80%E8%A6%81%E6%96%B0%E7%89%B9%E6%80%A7" class="hash-link" aria-label="为什么需要新特性？的直接链接" title="为什么需要新特性？的直接链接" translate="no">​</a></h2>
<p>在 Kotlin 中，<code>companion object</code> 一直是定义类级别常量、工厂方法和工具函数的标准方式。但它有几个痛点：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 旧写法：需要 @JvmStatic 才能让 Java 方便调用</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> x</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> y</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">object</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token annotation builtin">@JvmStatic</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> ZERO </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token annotation builtin">@JvmStatic</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fromPolar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theta</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Vector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> kotlin</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">cos</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">theta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> kotlin</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">sin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">theta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>每个 <code>@JvmStatic</code> 注解都是一份"税"——忘记写一个，Java 端就要多写一层 <code>Companion</code> 访问。而且 <code>companion object</code> 本质上是一个对象实例，JVM 上会额外生成一个类。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="伴生块更轻量的类级别成员">伴生块：更轻量的类级别成员<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E4%BC%B4%E7%94%9F%E5%9D%97%E6%9B%B4%E8%BD%BB%E9%87%8F%E7%9A%84%E7%B1%BB%E7%BA%A7%E5%88%AB%E6%88%90%E5%91%98" class="hash-link" aria-label="伴生块：更轻量的类级别成员的直接链接" title="伴生块：更轻量的类级别成员的直接链接" translate="no">​</a></h2>
<p>Kotlin 2.5.0 引入了 <code>companion {}</code> 块语法，直接生成 <strong>静态成员</strong>，不再创建对象实例：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 新写法：companion 块，编译为 JVM 静态成员</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> x</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> y</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> ZERO </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">fromPolar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> theta</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Double</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Vector </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> kotlin</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">cos</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">theta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r </span><span class="token operator" style="color:#393A34">*</span><span class="token plain"> kotlin</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">sin</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">theta</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>关键区别：</p>
<ul>
<li class=""><strong>不需要 <code>@JvmStatic</code></strong>：在 JVM 平台上，<code>companion</code> 块中的成员自动编译为静态方法和字段</li>
<li class=""><strong>不生成额外类</strong>：<code>companion object</code> 会生成一个单例类，而 <code>companion</code> 块不会</li>
<li class=""><strong>语法更简洁</strong>：少了 <code>object</code> 关键字，语义更明确</li>
</ul>
<p>Java 端可以直接调用：</p>
<div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// Java 代码中直接调用，无需 .Companion</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">Vector</span><span class="token plain"> v </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name">Vector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fromPolar</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token class-name">Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">PI</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">/</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token class-name">Vector</span><span class="token plain"> zero </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token class-name">Vector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">ZERO</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="伴生扩展给任意类型添加类级别成员">伴生扩展：给任意类型添加类级别成员<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E4%BC%B4%E7%94%9F%E6%89%A9%E5%B1%95%E7%BB%99%E4%BB%BB%E6%84%8F%E7%B1%BB%E5%9E%8B%E6%B7%BB%E5%8A%A0%E7%B1%BB%E7%BA%A7%E5%88%AB%E6%88%90%E5%91%98" class="hash-link" aria-label="伴生扩展：给任意类型添加类级别成员的直接链接" title="伴生扩展：给任意类型添加类级别成员的直接链接" translate="no">​</a></h2>
<p>更强大的能力是 <strong>伴生扩展</strong>。即使一个类没有定义 <code>companion object</code>，你也能通过伴生扩展为它添加类级别成员。这对 Java 类型尤其有用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 为 Java 的 LocalDate 添加伴生扩展</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">package</span><span class="token plain"> com</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">example</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dates</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> java</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">LocalDate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> LocalDate</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fromCompact</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> String</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> LocalDate </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">require</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length </span><span class="token operator" style="color:#393A34">==</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token string-literal singleline string" style="color:#e3116c">"格式应为 YYYYMMDD，共 8 位"</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> LocalDate</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">of</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">substring</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toInt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">substring</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">4</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toInt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        text</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">substring</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">6</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">8</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toInt</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>使用时只需导入：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> com</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">example</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">dates</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fromCompact</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> java</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">LocalDate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> date </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> LocalDate</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">fromCompact</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"20261013"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// 2026-10-13</span><br></div></code></pre></div></div>
<p><code>LocalDate</code> 是 Java 类，根本没有 Kotlin 的 <code>companion object</code>，但伴生扩展让我们像使用原生 Kotlin API 一样调用它。</p>
<p>伴生扩展属性也同样好用：</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 伴生扩展属性，支持 backing field</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> Vector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UnitX </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">val</span><span class="token plain"> Vector</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">UnitY </span><span class="token keyword" style="color:#00009f">get</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">Vector</span><span class="token punctuation" style="color:#393A34">(</span><span class="token number" style="color:#36acaa">0.0</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="如何启用">如何启用<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E5%A6%82%E4%BD%95%E5%90%AF%E7%94%A8" class="hash-link" aria-label="如何启用的直接链接" title="如何启用的直接链接" translate="no">​</a></h2>
<p>这个特性目前还是实验性的，需要手动开启。</p>
<p><strong>Gradle Kotlin DSL：</strong></p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">kotlin </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    compilerOptions </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 只启用伴生块</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        freeCompilerArgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"-Xcompanion-blocks"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token comment" style="color:#999988;font-style:italic">// 同时启用伴生扩展（会生成 pre-release 编译产物）</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        freeCompilerArgs</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">add</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"-Xcompanion-blocks-and-extensions"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p><strong>注意事项：</strong></p>
<ul>
<li class="">只启用伴生块（<code>-Xcompanion-blocks</code>）不影响编译产物的稳定性</li>
<li class="">启用伴生扩展（<code>-Xcompanion-blocks-and-extensions</code>）会生成 pre-release binaries，作为库发布时需注意下游兼容性</li>
<li class="">在 IntelliJ IDEA 2026.1+ 中，直接写 <code>companion {}</code> 会提示你启用该特性</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="与-companion-object-的对比">与 companion object 的对比<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E4%B8%8E-companion-object-%E7%9A%84%E5%AF%B9%E6%AF%94" class="hash-link" aria-label="与 companion object 的对比的直接链接" title="与 companion object 的对比的直接链接" translate="no">​</a></h2>
<table><thead><tr><th>特性</th><th><code>companion object</code></th><th><code>companion</code> 块</th><th>伴生扩展</th></tr></thead><tbody><tr><td>需要声明在类内部</td><td>是</td><td>是</td><td>否（顶层声明）</td></tr><tr><td>生成额外类</td><td>是</td><td>否</td><td>否</td></tr><tr><td>JVM 静态成员</td><td>需 <code>@JvmStatic</code></td><td>自动</td><td>自动</td></tr><tr><td>可扩展 Java 类型</td><td>否</td><td>否</td><td>是</td></tr><tr><td>可实现接口</td><td>是</td><td>否</td><td>否</td></tr><tr><td>有实例</td><td>是（单例）</td><td>否</td><td>否</td></tr><tr><td>属性支持 backing field</td><td>是</td><td>是</td><td>是</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="迁移建议">迁移建议<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E8%BF%81%E7%A7%BB%E5%BB%BA%E8%AE%AE" class="hash-link" aria-label="迁移建议的直接链接" title="迁移建议的直接链接" translate="no">​</a></h2>
<p>不需要急着把所有 <code>companion object</code> 替换掉。以下场景建议优先迁移：</p>
<ol>
<li class=""><strong>频繁被 Java 调用的代码</strong>：去掉所有 <code>@JvmStatic</code>，减少出错可能</li>
<li class=""><strong>纯工具类/常量类</strong>：不需要实现接口、不需要单例实例的场景</li>
<li class=""><strong>需要为 Java 类型添加工具方法</strong>：用伴生扩展替代顶层函数，API 更直观</li>
</ol>
<p>以下场景暂时保留 <code>companion object</code>：</p>
<ul>
<li class="">companion object 实现了接口</li>
<li class="">代码中依赖 companion object 的单例特性</li>
<li class="">需要保持库的二进制兼容性</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="总结">总结<a href="https://www.cqkug.com/2026/10/13/kotlin-companion-blocks-and-extensions#%E6%80%BB%E7%BB%93" class="hash-link" aria-label="总结的直接链接" title="总结的直接链接" translate="no">​</a></h2>
<p>伴生块和伴生扩展是 Kotlin 语言在"简洁"和"实用"方向上的又一次进化。它解决了 <code>companion object</code> 长期存在的 JVM 互操作痛点，同时通过伴生扩展打开了为任意类型（包括 Java 类型）添加类级别成员的大门。</p>
<p>虽然目前还是实验性特性，但设计已经相当成熟（KEEP-0449），预计在未来版本中会逐步稳定。建议大家现在就开始在项目中试用，提前感受 Kotlin 语言演进的节奏。</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// 一句话总结</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* 更简洁的类级别成员 */</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">companion</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> Type</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">newMember</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* 给任意类型加成员 */</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<blockquote>
<p>参考资料：</p>
<ul>
<li class=""><a href="https://blog.jetbrains.com/kotlin/2026/09/the-companions-to-come" target="_blank" rel="noopener noreferrer" class="">The Companions to Come - JetBrains Blog</a></li>
<li class=""><a href="https://github.com/Kotlin/KEEP/blob/main/proposals/KEEP-0449-companions-block-extension.md" target="_blank" rel="noopener noreferrer" class="">KEEP-0449: Companion Blocks and Extensions</a></li>
<li class=""><a href="https://kotlinlang.org/docs/whatsnew-eap.html" target="_blank" rel="noopener noreferrer" class="">What's new in Kotlin 2.5.0-Beta1</a></li>
</ul>
</blockquote>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="Kotlin" term="Kotlin"/>
        <category label="Kotlin 2.5" term="Kotlin 2.5"/>
        <category label="伴生块" term="伴生块"/>
        <category label="伴生扩展" term="伴生扩展"/>
        <category label="companion object" term="companion object"/>
        <category label="语言特性" term="语言特性"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[CQKUG 社区网站上线]]></title>
        <id>https://www.cqkug.com/2026/10/03/hello-cqkug</id>
        <link href="https://www.cqkug.com/2026/10/03/hello-cqkug"/>
        <updated>2026-10-03T02:00:00.000Z</updated>
        <summary type="html"><![CDATA[重庆 Kotlin User Group 官方网站正式上线]]></summary>
        <content type="html"><![CDATA[<p>大家好，重庆 Kotlin User Group（CQKUG）的社区网站正式上线了！</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="这里会有什么">这里会有什么<a href="https://www.cqkug.com/2026/10/03/hello-cqkug#%E8%BF%99%E9%87%8C%E4%BC%9A%E6%9C%89%E4%BB%80%E4%B9%88" class="hash-link" aria-label="这里会有什么的直接链接" title="这里会有什么的直接链接" translate="no">​</a></h2>
<p>这个网站将承载社区的全部公开内容：</p>
<ul>
<li class=""><strong>技术博客</strong>：Kotlin 语言、协程、Compose、KMP、服务端开发等原创文章</li>
<li class=""><strong>活动公告</strong>：meetup、沙龙的信息与回顾</li>
<li class=""><strong>社区资源</strong>：分享沉淀下来的资料与链接</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="为什么用-kotlin-写博客">为什么用 Kotlin 写博客<a href="https://www.cqkug.com/2026/10/03/hello-cqkug#%E4%B8%BA%E4%BB%80%E4%B9%88%E7%94%A8-kotlin-%E5%86%99%E5%8D%9A%E5%AE%A2" class="hash-link" aria-label="为什么用 Kotlin 写博客的直接链接" title="为什么用 Kotlin 写博客的直接链接" translate="no">​</a></h2>
<p>当然，网站本身并不是 Kotlin 写的，但作为 Kotlin 社区，我们随时欢迎用 Kotlin 相关的工具链来建设社区设施。如果你有兴趣参与网站建设，欢迎在仓库提 issue 或 PR。</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="下一步">下一步<a href="https://www.cqkug.com/2026/10/03/hello-cqkug#%E4%B8%8B%E4%B8%80%E6%AD%A5" class="hash-link" aria-label="下一步的直接链接" title="下一步的直接链接" translate="no">​</a></h2>
<ul>
<li class="">完善组织者与联系方式信息</li>
<li class="">发布第一篇技术投稿</li>
<li class="">筹备第一次线下 meetup</li>
</ul>
<p>欢迎重庆及西南地区的朋友们关注与加入！</p>
<div class="language-kotlin codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-kotlin codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">fun</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">main</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token function" style="color:#d73a49">println</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string-literal singleline string" style="color:#e3116c">"Hello, CQKUG!"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>]]></content>
        <author>
            <name>CQKUG 组织团队</name>
            <uri>https://www.cqkug.com</uri>
        </author>
        <category label="社区公告" term="社区公告"/>
    </entry>
</feed>