← 回到首頁

每日新聞摘要

2026年01月23日

選擇語言 / Select Language

中文 English 简体中文
今日收集到 27 篇文章摘要

Ditch the Cloud: This Open-Source AI Tracker Reads Your Bank SMS, 100% Private

Ditch the Cloud: This Open-Source AI Tracker Reads Your Bank SMS, 100% Private
An open-source Android app that parses bank SMS with on-device AI to track expenses while keeping all data local.

重點摘要

Parses bank SMS on-device to automatically track expenses.
Performs 100% local processing — no data is sent to the cloud.
Open-source Kotlin project supporting 40+ banks, multi-currency parsing, and an on-device AI assistant.
PennyWise AI is a privacy-focused Android application that automatically tracks expenses by parsing bank transaction SMS messages entirely on-device. The app intercepts individual SMS notifications (without modifying the inbox or sending messages) and uses local AI models to extract key details such as amount, merchant, category and date. It detects recurring subscriptions, supports multi-currency tracking and parsing for 40+ banks across multiple countries, and includes an on-device natural-language AI assistant for queries like spending by category or merchant. The project is open-source and written in Kotlin, with community metrics (354 stars, 56 forks, 22 open issues), and serves as a practical reference for developers building edge machine-learning solutions that prioritize user data privacy while automating financial recordkeeping.
摆脱云端:这款开源 AI 读取你的银行短信,100% 私密
一款在设备上用 AI 解析银行短信、完全本地处理并追踪支出的开源 Android 应用。

重點摘要

在设备端解析银行短信,自动记录消费。
所有智能处理均在本机完成,不会将数据上传云端。
开源 Kotlin 项目,支持 40+ 家银行、多币种和离线 AI 助手。
PennyWise AI 是一款以隐私为先的 Android 开源应用,通过设备端 AI 自动解析银行交易短信,提取金额、商户、类别与日期,并不会修改收件箱或将信息发送到云端。它支持 40 多家银行与多币种、多区域的解析,能够自动检测订阅服务并生成清晰的可搜索收支时间线。内置的离线 AI 助手支持自然语言查询消费记录,简化个人理财流程。源代码以 Kotlin 编写,适合作为实现边缘机器学习与隐私保护功能的实务参考(项目星数 354、分支 56、开启议题 22)。
擺脫雲端:這款開源 AI 讀取你的銀行簡訊,100% 隱私
一款在裝置上用 AI 解析銀行簡訊、完全在地處理並追蹤支出的開源 Android 應用。

重點摘要

在裝置端解析銀行簡訊,自動追蹤消費。
所有處理均於本機完成,不會將資料上傳雲端。
開源 Kotlin 專案,支援 40+ 家銀行、多幣別與離線 AI 助手。
PennyWise AI 是一款隱私導向的 Android 開源應用,透過裝置端的 AI 自動解析銀行交易簡訊,擷取金額、商家、類別與日期,且不會修改信箱或上傳任何資料到雲端。它支援超過 40 家銀行與多幣別、多國區域的解析,能自動偵測訂閱服務並將分散的簡訊轉為可搜尋的收支時序。內建的離線 AI 助手允許使用自然語言查詢消費紀錄,降低理財管理的摩擦。原始碼以 Kotlin 撰寫,對想在邊緣機器學習與隱私優先應用上實作的開發者具實務參考價值(專案星數 354、分支 56、開啟議題 22)。

Why I Put a Wall Between Domain and UI (And Called It a Mapper)

Why I Put a Wall Between Domain and UI (And Called It a Mapper)
I put a wall between the domain and the UI and called that boundary a mapper.

重點摘要

Introduces a deliberate mapping layer between domain and UI
Aims to decouple UI concerns from domain models
Part of the StreamBox branch-by-branch series
This post, part of the StreamBox branch-by-branch series, explains the author's decision to add an explicit boundary between the domain layer and the user interface, which he terms a 'mapper.' The mapper's role is to translate or adapt domain models into UI-friendly representations and to prevent UI-driven changes from leaking into core business logic. By treating mapping as a distinct responsibility, the pattern encourages separation of concerns, reduces coupling between layers, and makes both domain logic and UI code easier to evolve and test independently. The article focuses on the rationale for this architectural wall and why naming and enforcing that boundary helps maintain clarity and robustness in application design.
为何我在领域与 UI 之间建了一道墙(并称之为 Mapper)
我在领域与 UI 之间建立了一道墙,并称那道边界为 mapper。

重點摘要

在领域层和 UI 之间设立独立的映射层
防止 UI 需求影响到核心业务模型
该文章属于 StreamBox branch-by-branch 系列
本文是 StreamBox branch-by-branch 系列的一篇,作者解释了为什么在领域(Domain)与用户界面(UI)之间添加一个明确的边界,并将其称为“mapper”。mapper 的职责是将领域模型转换为适合 UI 使用的表示,阻止 UI 导向的改动渗入核心业务逻辑。把映射作为独立责任有助于分离关注点、降低各层之间的耦合,并使领域逻辑与界面代码更易于独立演进与测试。文章重点阐述了建立并命名该边界的理由,以及它如何提高应用架构的清晰性和稳健性。
為何我在 Domain 與 UI 之間築起一道牆(並稱之為 Mapper)
我在 Domain 與 UI 之間建立一道牆,並稱那道界面為 mapper。

重點摘要

在 Domain 與 UI 之間引入明確的映射層
避免 UI 的需求污染核心商業邏輯
此文為 StreamBox branch-by-branch 系列的一部分
本文為 StreamBox branch-by-branch 系列的一篇,作者說明為何在領域層(Domain)與使用者介面(UI)之間設置一個明確的邊界,並將其命名為「mapper」。這個 mapper 負責將領域模型轉換成適合 UI 使用的表示,並阻止 UI 驅動的變動侵入核心商業邏輯。將映射視為獨立職責,有助於界定關注點、降低層與層之間的耦合,並使領域邏輯與介面實作能各自更容易演進與測試。文章重點在於闡述設置與命名此牆的理由,以及它如何提升設計的清晰度與穩健性。

Exposed 1.0 Is Now Available

Exposed 1.0 Is Now Available
Exposed 1.0 introduces a stable API, R2DBC support, performance improvements, and continued Spring integration.

重點摘要

Adds R2DBC support for reactive database drivers
Stable 1.0 API guaranteeing no breaking changes until next major release
Improved Spring integration including Boot 3 & 4 support and GraalVM native image improvements
Exposed 1.0 is the first major release of the Kotlin SQL library, marking a significant milestone with a stable API that promises no breaking changes until the next major version. The release adds highly requested R2DBC support, enabling reactive drivers to work with Exposed’s SQL DSL, plus performance enhancements and bug fixes. Spring users benefit from continued support for Spring Boot 3 and compatibility with Spring Boot 4, better transaction management, and GraalVM native image improvements. The project provides examples for Ktor and Spring, a migration guide for moving from version 0.61.0 to 1.0, and encourages joining the #exposed channel on Kotlin Slack for questions. The announcement also highlights a KotlinConf 2025 talk by Chantal Loncle and thanks numerous contributors whose pull requests were included.
Exposed 1.0 现已发布
Exposed 1.0 引入稳定 API、支持 R2DBC,并带来性能改进与更好的 Spring 集成体验。

重點摘要

引入 R2DBC 支持以兼容反应式数据库驱动
1.0 提供稳定 API,保证至下一个大版本前不出现破坏性变更
改进 Spring 集成,支持 Spring Boot 3 与 4,优化事务管理与 GraalVM 原生镜像
Exposed 1.0 是該專案的首個重大版本,具有里程碑意義:發布了穩定的 API,承諾在下一个 major 版本前不會有破坏性变更;同時加入社群高度期待的 R2DBC 支持,使反应式驱动能与 Exposed 的 SQL DSL 配合使用。版本還包含性能提升與修复,继续支持 Spring Boot 3 并兼容 Spring Boot 4,改善事务管理并增强 GraalVM 原生镜像支持。官方提供从 0.61.0 到 1.0 的迁移指南、Ktor 与 Spring 示例,并邀请开发者加入 Kotlin Slack 的 #exposed 频道交流,同时向为本次发布贡献代码的众多贡献者致谢。
Exposed 1.0 現已釋出
Exposed 1.0 推出穩定 API、加入 R2DBC 支援並帶來效能改進與 Spring 整合優化。

重點摘要

新增 R2DBC 支援以使用反應式資料庫驅動
1.0 提供穩定 API,保證到下一個 major 版本前不會有破壞性變更
強化 Spring 整合,支援 Spring Boot 3 與 4、改善交易管理與 GraalVM 原生映像支援
Exposed 1.0 為該專案的第一個主要版本,具里程碑意義:提供穩定的 API、社群高度要求的 R2DBC 支援,並包含多項效能提升與錯誤修正。R2DBC 的加入讓 Exposed 的 SQL DSL 可搭配反應式驅動使用;對於使用 Spring 的開發者,1.0 持續支援 Spring Boot 3 並兼容 Spring Boot 4,同時改進交易管理與 GraalVM 原生映像支援。文件提供從 0.61.0 遷移到 1.0 的指南、Ktor 與 Spring 範例,並鼓勵在 Kotlin Slack 的 #exposed 頻道交流,公告也對所有貢獻者表達感謝。

Ktor 3.4.0 Is Now Available!

Ktor 3.4.0 Is Now Available!
Ktor 3.4.0 improves stability and adds OpenAPI generation, OkHttp duplex streaming, Zstd compression, and an HttpRequestLifecycle plugin.

重點摘要

Runtime OpenAPI generation with describe API and compiler plugin
OkHttp engine supports duplex streaming for HTTP/2
Zstd compression support and HttpRequestLifecycle to cancel in-flight requests
Ktor 3.4.0 focuses on stability and fixes while adding several notable features. It completes OpenAPI document generation by introducing a runtime describe API and a compiler plugin (and integration via the Ktor Gradle plugin) so Swagger models are produced from routing metadata. The OkHttp client engine gains duplex streaming for HTTP/2, allowing simultaneous request-body send and response receive. Compression plugin adds Zstd support via ktor-server-compression-zstd with configurable compressionLevel. The new HttpRequestLifecycle plugin cancels in-flight requests when clients disconnect (cancelCallOnClose = true), leveraging structured concurrency to clean up coroutines; currently supported on Netty and CIO engines. The release emphasizes performance and invites developers to join the Ktor Early Access Program and start projects at start.ktor.io. The blog thanks the community for feedback and issue reports.
Ktor 3.4.0 现已发布!
本次发布强调稳定性,并新增从代码生成 OpenAPI、OkHttp 双向流、Zstd 压缩和 HttpRequestLifecycle 插件等功能。

重點摘要

新增从代码生成 OpenAPI(describe API 与编译器插件)
OkHttp 引擎支持 HTTP/2 双向流(duplex streaming)
新增 Zstd 压缩支持与在客户端断开时取消请求的 HttpRequestLifecycle
Ktor 3.4.0 以提升稳定性与修复长期问题为重点,同时引入多项功能:通过新增的 describe API 与编译器插件(并可由 Ktor Gradle 插件启用),实现了运行时由路由信息生成的 OpenAPI 文档;OkHttp 客户端引擎新增对 HTTP/2 的双向流,允许在发送请求体的同时接收响应;Compression 插件通过 ktor-server-compression-zstd 模块添加了 Zstd 支持并可配置压缩等级;HttpRequestLifecycle 插件可在客户端断开时取消正在进行的请求并利用结构化并发清理资源(当前支持 Netty 与 CIO 引擎)。团队也邀请开发者加入 Early Access Program 并在 start.ktor.io 启动新项目,感谢社区的反馈与问题报告。
Ktor 3.4.0 現已釋出!
本次釋出強調穩定性,並加入從程式碼產生 OpenAPI、OkHttp 的雙向串流、Zstd 壓縮與 HttpRequestLifecycle 等功能。

重點摘要

新增從程式碼產生 OpenAPI(describe API 與編譯器外掛)
OkHttp 引擎支援 HTTP/2 雙向串流(duplex streaming)
加入 Zstd 壓縮支援與可在客戶端斷線時取消請求的 HttpRequestLifecycle
Ktor 3.4.0 以提高穩定性和修正長期問題為主軸,同時完成多項重要功能:引入 describe API 與編譯器外掛,讓 OpenAPI 文件可於執行時由路由資訊動態生成,並可透過 Ktor Gradle 外掛啟用;OkHttp 客戶端引擎新增對 HTTP/2 的雙向串流,允許在傳送請求主體的同時接收回應;Compression 外掛新增 ktor-server-compression-zstd 模組以支援 Zstd 並可設定壓縮等級;HttpRequestLifecycle 外掛可在客戶端斷線時取消進行中的請求,並運用結構化併發清理資源(目前支援 Netty 與 CIO 引擎)。團隊也邀請開發者加入 Early Access Program 並在 start.ktor.io 開始專案,感謝社群回饋與回報問題。

Java Weekly, Issue 629

Java Weekly, Issue 629
Functional Optics for Modern Java (Part 1) addresses the painful copy-constructor cascade for immutable updates by letting developers compose access paths and update nested fields with single expressions.

重點摘要

Functional Optics simplifies concise updates to nested immutable data in Java
Curated links on JVM improvements, build optimization, AI/agent patterns, and testing
Notable releases: Spring gRPC 1.0.1, Quarkus 3.30.6, Grails 7.0.5, Apache Camel updates
This issue of Java Weekly highlights Functional Optics for Modern Java (Part 1), which improves how developers update nested immutable data by composing access paths to avoid long copy-constructor cascades. The newsletter also collects recent Java and cloud-native coverage—AOT cache optimizations, reduced sampling costs, faster builds, virtual thread states, Spring AI agentic patterns, warmup and scaling topics, flaky-test strategies, and guidance for choosing LLMs. It lists webinars and presentations on Java's 2026 plans, the Static Dynamic JVM, Spring podcasts, concurrency lessons, and SRE AI agents. Several upgrades are called out for immediate attention, including Spring gRPC 1.0.1, Quarkus 3.30.6, Netflix Zuul, Grails, and Apache Camel. The Pick of the Week promotes a hands-on Spring Boot live webinar aimed at teams working across Boot 2.x–4.x.
Java Weekly,第629期
《Functional Optics for Modern Java(第1部分)》通过组合访问路径,使嵌套不可变数据的字段更新从繁琐的拷贝构造流程变为单表达式。

重點摘要

Functional Optics 让不可变嵌套数据更新更简洁
汇总 JVM 优化、构建加速、AI 代理模式与测试相关文章
建议升级:Spring gRPC 1.0.1、Quarkus 3.30.6、Grails 7.0.5 等
本期主要介绍 Functional Optics for Modern Java(第一部分),该方法弥补了模式匹配在读取嵌套不可变数据方面的不足,使更新操作无需层层拷贝构造即可以单一表达式完成。周刊还收集了多篇值得关注的技术文章,涉及 Java 的 AOT 缓存优化、采样成本显著降低、构建加速、虚拟线程状态、Spring AI 代理模式、启动与热身问题、易碎测试的对策以及如何为云原生时代优化 Java。名单中还包含多场网络研讨会与演讲,以及若干重要组件的版本更新,推荐参加一场针对 Spring Boot(2.x、3.x 及 4.x 规划)的实务直播活动作为本周精选。
Java Weekly,第629期
《Functional Optics for Modern Java(第一部)》透過組合存取路徑,讓巢狀不可變資料的欄位更新由冗長的拷貝構造流程變成單一表達式。

重點摘要

Functional Optics 簡化巢狀不可變資料的更新
彙整 JVM、建置優化、AI 代理模式與測試相關重要文章
建議升級:Spring gRPC 1.0.1、Quarkus 3.30.6、Grails 7.0.5 等
本期彙整重點包括 Functional Optics for Modern Java(第一部),該作法彌補了模式匹配在讀取巢狀不可變資料時的不足,讓更新操作不再依賴層層拷貝構造。週報同時列出多篇值得閱讀的技術文章,涵蓋 Java 的 AOT 快取優化、採樣成本大幅降低、加速建置、虛擬執行緒狀態、Spring AI 代理模式、啟動與暖身、易碎測試處理、以及如何為雲原生環境優化 Java。另收錄數場線上研討會與發表,以及數個重要元件的新版本釋出,並推薦一場針對 Spring Boot(2.x 到 4.x 規劃)的實作型直播訓練為本週精選。

How to Automate Hidden and Read-Only Inputs with Selenium and JavaScript

How to Automate Hidden and Read-Only Inputs with Selenium and JavaScript
Because sendKeys() simulates keyboard events and cannot interact with hidden or read-only fields, update those inputs directly via JavascriptExecutor by modifying the DOM.

重點摘要

Selenium simulates real user typing and won't interact with hidden/disabled fields
Use JavascriptExecutor to set input.value on the DOM for hidden or script-controlled fields
Example includes a Spring Boot backend, an HTML form, and Selenium code that verifies server-received values
This tutorial demonstrates how to handle hidden and read-only input fields in web automation by combining Selenium with direct JavaScript DOM manipulation. It explains that Selenium's sendKeys() emulates keyboard events and therefore cannot operate on elements a human cannot interact with, like hidden inputs or fields controlled by scripts. The article shows a complete setup: Maven dependencies (selenium-java and spring-boot-starter-web), a Spring Boot application exposing an HTML form, a controller that receives form submissions, and a Selenium script. The Selenium code fills visible fields with sendKeys() and uses JavascriptExecutor to assign a value to a hidden input before submitting the form; the server console output verifies the values were received. The guide concludes that using JavascriptExecutor is a deliberate, necessary technique to ensure automated tests include all data the server expects.
使用 Selenium 与 JavaScript 自动处理隐藏和只读输入字段
由于 sendKeys() 模拟键盘事件,无法操作隐藏或只读字段,因此可通过 JavascriptExecutor 直接修改 DOM 来设置这些输入的 value。

重點摘要

Selenium 模拟真实用户输入,无法触及隐藏或被禁用的元素
使用 JavascriptExecutor 在 DOM 上直接设置 input.value 以填充隐藏字段
示例包含 Spring Boot 后端、HTML 表单与 Selenium 自动化及服务器端验证
本文展示了如何在自动化测试中处理隐藏与只读输入字段——关键在于理解 Selenium 的 sendKeys() 是模拟键盘事件,因而不能操作用户无法触及的元素。文章提供完整示例:Maven 依赖(selenium-java 与 spring-boot-starter-web)、在 localhost:8080 运行的 Spring Boot 应用、用于接收表单的控制器和 HTML 页面,以及 Selenium 脚本。示例对可见字段使用 sendKeys(),对隐藏字段通过 JavascriptExecutor 直接修改 DOM 的 value,然后提交表单,服务器控制台的输出用以验证接收到的值。结论指出,对于隐藏或由脚本填充的字段,直接在 DOM 上用 JavaScript 设置值是必要且恰当的方式。
使用 Selenium 與 JavaScript 自動處理隱藏與唯讀輸入欄位
因為 sendKeys() 模擬鍵盤事件,無法操作隱藏或唯讀欄位,因此可透過 JavascriptExecutor 直接修改 DOM 來設定這些輸入的 value。

重點摘要

sendKeys() 模擬使用者輸入,無法觸及隱藏或被禁用的元素
利用 JavascriptExecutor 在 DOM 上直接設定 input.value 以填寫隱藏欄位
示範包含 Spring Boot 後端、HTML 表單與 Selenium 自動化與伺服器端驗證
本文示範如何在自動化測試中處理隱藏與唯讀的輸入欄位:說明 Selenium 的 sendKeys() 是模擬鍵盤事件,因而無法操作人類無法互動的元素。作者提供完整實作:Maven 相依(selenium-java 與 spring-boot-starter-web)、啟動於 localhost:8080 的 Spring Boot 應用、呈現表單的 Controller,以及 Selenium 腳本。範例中對可見欄位使用 sendKeys(),對隱藏欄位則使用 JavascriptExecutor 直接寫入 DOM 的 value,再送出表單。伺服器端透過 console 輸出確認接收到所有值。結論是對於不可見或被腳本控制的欄位,透過 JavaScript 修改 DOM 是必要且正確的做法。

How to Resolve NaN Values in Micrometer Gauges in Prometheus

How to Resolve NaN Values in Micrometer Gauges in Prometheus
Micrometer Gauges use weak references by default, so a monitored object can be garbage-collected and the gauge will report NaN when scraped.

重點摘要

Micrometer Gauge stores a weak reference to the monitored object by default
Objects garbage-collected by the JVM cause gauges to report NaN
Fix by keeping a strong reference or using Gauge.Builder.strongReference(true)
This article demonstrates why Micrometer gauges can return NaN when exposing in-memory Java objects to Prometheus and how to fix it. By default, Micrometer avoids memory leaks by holding weak references to monitored objects; if the object is only weakly reachable it can be reclaimed by the JVM, leaving the gauge unable to call its value function and reporting NaN. The tutorial reproduces the issue using Spring Boot Actuator, inspects the root cause (interaction between Java garbage collection and Micrometer’s weak-reference design), and offers solutions: maintain a strong reference to the monitored object (for example, store it as a field) or enable strong references explicitly with Gauge.Builder.strongReference(true). With these changes, metrics remain stable and report actual values instead of NaN.
在 Prometheus 中解决 Micrometer Gauge 返回 NaN 的问题
Micrometer 的 Gauge 默认使用弱引用,如果被监控对象被垃圾回收,Gauge 在抓取时会返回 NaN。

重點摘要

Micrometer 默认以弱引用保存被监控对象
被垃圾回收的对象会导致 Gauge 报告 NaN
通过保留强引用或使用 .strongReference(true) 即可修复
本文展示了 Micrometer Gauge 在暴露内存对象为指标时为何会出现 NaN 以及如何解决。Micrometer 为避免内存泄漏,默认对被监控对象使用弱引用;若对象在方法执行结束后没有其他强引用,JVM 会回收该对象,导致 Gauge 在被抓取时无法访问对象而返回 NaN。作者以 Spring Boot Actuator 重现该问题,解析根因(Java 垃圾回收与 Micrometer 弱引用的交互),并演示修复方法:将对象保存为类字段以维持强引用,或在 Gauge.Builder 中启用 .strongReference(true)。应用这些方法可确保指标对 Prometheus 等监控系统保持准确可靠。
在 Prometheus 中解決 Micrometer Gauge 顯示 NaN 的問題
Micrometer 的 Gauge 預設使用弱參考,若被監控的物件被垃圾回收,Gauge 在被抓取時會回傳 NaN。

重點摘要

Micrometer 預設以弱參考儲存被監控物件
被垃圾回收的物件會導致 Gauge 顯示 NaN
可透過保留強參考或使用 .strongReference(true) 修正
本文說明為何 Micrometer 的 Gauge 在監控應用內部物件時會出現 NaN,以及如何修正。Micrometer 為避免記憶體洩漏,預設對被監控物件使用弱參考;若物件在方法結束後沒有其他強參考,JVM 可能會回收該物件,之後 Gauge 無法取得值函式的物件參考,進而回傳 NaN。文章以 Spring Boot Actuator 重現此情境,分析原因,並提供解法:將被監控物件存為類別欄位保持強參考,或在 Gauge 建構時呼叫 .strongReference(true)。採用這些方式可確保暴露給 Prometheus 的度量保持正確與可靠。

Getting Started with Open J Proxy (OJP)

Getting Started with Open J Proxy (OJP)
OJP acts as a Type 3 JDBC driver and a remote Layer 7 database proxy, so applications send JDBC operations to the OJP Server which centrally manages pooling.

重點摘要

Type 3 JDBC driver that proxies DB access and centralizes pooling
Driver returns virtual connections; actual statements go to OJP Server over gRPC
Quickstart: run PostgreSQL and OJP in Docker, remove HikariCP, add ojp-jdbc-driver, use SimpleDriverDataSource
This tutorial demonstrates integrating Open J Proxy (OJP) with a Spring Boot application using a local PostgreSQL and Dockerized OJP server. OJP provides a Type 3 JDBC driver and Layer 7 proxy: applications obtain virtual JDBC connections while the driver forwards executed statements to the OJP Server via gRPC. The server owns and pools real database connections centrally, reducing connection storms and improving resource sharing across services. Steps include running PostgreSQL and the OJP server in Docker, creating a Spring Boot project, excluding HikariCP, adding the ojp-jdbc-driver dependency, and configuring application.properties to use the ojp[host:port]_postgresql URL and SimpleDriverDataSource so pooling is delegated to OJP. A small CRUD example verifies SQL flows through OJP. The guide notes OJP’s compatibility with other JVM frameworks and multiple databases and suggests multinode, serverless, or non-Java gRPC integrations as next steps.
入门 Open J Proxy (OJP)
OJP 作为 Type 3 JDBC 驱动和 Layer 7 远端数据库代理,应用将 JDBC 操作发送到 OJP Server,由服务器集中管理连接池。

重點摘要

Type 3 驱动 + Layer 7 代理,集中管理后端数据库连接
驱动返回虚拟连接,实际语句通过 gRPC 转发至 OJP Server
流程:运行 PostgreSQL 与 OJP Docker、移除 HikariCP、加入 ojp-jdbc-driver、使用 SimpleDriverDataSource
本教程示范如何在 Spring Boot 应用中整合 Open J Proxy (OJP),以本地 PostgreSQL 和 Docker 化的 OJP 服务器为例。OJP 作为 Type 3 JDBC 驱动与 Layer 7 远端代理,应用获取虚拟连接,实际 SQL 在执行时通过 gRPC 发给 OJP Server,由服务器统一管理后端连接与连接池,从而降低连接风暴并稳定数据库负载。指南包括启动 PostgreSQL 与 OJP 容器、创建 Spring Boot 项目、排除 HikariCP、添加 ojp-jdbc-driver 依赖,并在 application.properties 中使用 ojp[host:port]_postgresql URL 和 SimpleDriverDataSource,以将连接池职责委托给 OJP。通过简单 CRUD 示例验证数据流,且说明 OJP 支持多种框架与数据库,并提出后续的部署与整合方向。
快速上手 Open J Proxy (OJP)
OJP 擔任 Type 3 JDBC 驅動與 Layer 7 遠端資料庫代理,應用程式將 JDBC 操作送到 OJP Server,由伺服器集中管理連線池。

重點摘要

以 Type 3 JDBC 驅動與遠端代理集中管理後端連線
驅動回傳虛擬連線,真正的 SQL 執行透過 gRPC 傳給 OJP Server
步驟包括啟動 PostgreSQL 與 OJP Docker、移除 HikariCP、加入 ojp-jdbc-driver、使用 SimpleDriverDataSource
本教學示範如何在 Spring Boot 應用中整合 Open J Proxy (OJP),以本機 PostgreSQL 與 Docker 化的 OJP 伺服器為例。OJP 擔任 Type 3 JDBC 驅動與 Layer 7 遠端代理,應用程式取得虛擬連線,實際 SQL 在執行時經由 gRPC 傳給 OJP Server,由伺服器統一管理後端連線與連線池,能減少連線風暴並提升資源共享。教學步驟包含啟動 PostgreSQL 與 OJP 容器、建立 Spring Boot 專案、移除 HikariCP、加入 ojp-jdbc-driver,以及在 application.properties 使用 ojp[host:port]_postgresql 格式與 SimpleDriverDataSource 將 pooling 委由 OJP 管理。以簡單 CRUD 範例驗證資料流,並說明 OJP 支援多種框架與資料庫,與後續部署方向。

Building AI Agents Using Google Agent Development Kit (ADK)

Building AI Agents Using Google Agent Development Kit (ADK)
The Google ADK is an open-source Java framework for building agents that orchestrate LLMs and external tools, demonstrated here by a conversational agent named Baelgent.

重點摘要

Google ADK provides Java-centric primitives to wrap LLMs and build agents
Session management with InMemoryRunner preserves conversation context across requests
Function calling via FunctionTool lets the agent invoke Java methods (e.g., AuthorFetcher.fetch)
This tutorial demonstrates how to build a conversational AI agent with the Google Agent Development Kit (ADK) in the Java ecosystem. It walks through adding the google-adk dependency, meeting runtime requirements (Java 17+, setting GOOGLE_API_KEY), and defining a system prompt that shapes the agent's persona (Baelgent: a concise, Java/Spring-flavored responder). Configuration is handled via @ConfigurationProperties and application.yaml where the example uses the gemini-2.5-flash model. The agent is constructed as a BaseAgent using LlmAgent.builder(), and an AgentService uses InMemoryRunner plus an in-memory session cache to maintain conversation history across interactions. The ADK's function-calling capability is shown by registering an AuthorFetcher.fetch method with FunctionTool, enabling the agent to call Java code and incorporate results. Finally, a REST endpoint (/api/v1/agent/interact) exposes the agent and example requests illustrate persona, context continuity, and tool invocation.
使用 Google Agent Development Kit (ADK) 构建 AI 代理
Google ADK 是一个开源的 Java 框架,用于构建在 LLM 与外部工具之间协调复杂工作流的代理,示例代理名为 Baelgent。

重點摘要

在项目中加入 google-adk,运行需 Java 17+ 并通过 GOOGLE_API_KEY 提供 API Key
使用 InMemoryRunner 与会话缓存实现会话管理以保留上下文
通过 FunctionTool 注册 Java 方法(如 AuthorFetcher.fetch)以启用函数调用
本文介绍如何使用 Google ADK 在 Java 环境中构建会话式 AI 代理 Baelgent。步骤包括在 pom.xml 添加 google-adk 依赖、确保 Java 17 以上并设置 GOOGLE_API_KEY 环境变量,建立 system prompt 来定义代理的角色(以 Java/Spring 术语简洁回答),并在 application.yaml 与 @ConfigurationProperties 中配置代理名称、描述与所用模型(示例为 gemini-2.5-flash)。通过 LlmAgent.builder() 构造 BaseAgent,并在服务层使用 InMemoryRunner 配合并发哈希表缓存 session,以维持多次交互的会话上下文。示例还展示了如何用 FunctionTool 注册 AuthorFetcher.fetch,使模型在需要时调用 Java 方法并将结果纳入回复,最终通过 /api/v1/agent/interact 暴露 REST API 进行交互。
使用 Google Agent Development Kit (ADK) 建構 AI 代理
Google ADK 是一個開放原始碼的 Java 框架,可用來建構能在 LLM 與外部工具間協調複雜工作流程的代理,範例代理為 Baelgent。

重點摘要

在 pom.xml 加入 google-adk,並使用 Java 17+ 與 GOOGLE_API_KEY
以 InMemoryRunner 與快取的 session 保持會話上下文
透過 FunctionTool 註冊 Java 方法,讓模型能呼叫外部工具
本文示範如何在 Java 生態下使用 Google ADK 建置一個會話型 AI 代理 Baelgent。首先加入 google-adk 依賴、確保 Java 17 以上並以環境變數提供 Gemini API key;接著建立 system prompt 定義代理個性(以 Java/Spring 用語簡潔回應)。透過 @ConfigurationProperties 與 application.yaml 設定代理屬性(範例採 gemini-2.5-flash),再以 LlmAgent.builder() 建立 BaseAgent。服務層使用 InMemoryRunner 與 ConcurrentHashMap 快取 session,實作 interact() 來維持跨請求的對話歷史。此外示範如何用 FunctionTool 註冊 AuthorFetcher.fetch,使代理在需要時呼叫 Java 方法取得資料。最後透過 /api/v1/agent/interact 暴露 API,範例請求顯示個性化回應、上下文延續與工具調用功能。

Introduction to Payara

Introduction to Payara
Payara Server is a GlassFish fork that provides a full Jakarta EE–compliant runtime and tools for developing, deploying, and administering enterprise Java applications.

重點摘要

Payara Server supports Jakarta EE and MicroProfile with Full and Web Community distributions
Administration via asadmin covers domain lifecycle (start/stop/restart/create) and deployment
Deployments can be packaged as WAR, tested with a simple HelloPayara servlet, or managed through the Admin Console
This article explains how to download, install, run, and deploy applications on Payara Server. Payara is a fork of GlassFish that implements the Jakarta EE platform and can also run MicroProfile applications. The Community Edition is available in Full and Web Profile distributions. The server is managed with the asadmin command-line tool, which handles domain lifecycle operations (start-domain, stop-domain, restart-domain, create-domain) and deployments; the default domain is domain1. A basic example shows adding jakarta.servlet-api as a provided dependency, packaging the project as a WAR, creating a HelloPayara servlet that responds at /hello, and building with mvn clean package. Applications may be deployed via asadmin deploy or through the Admin Console at http://localhost:4848. The article also contrasts Payara’s full-featured enterprise capabilities with lighter servers like Tomcat, noting a larger footprint and that Payara Enterprise requires a paid subscription.
Payara 入门
Payara Server 是从 GlassFish 分支出的完整 Jakarta EE 运行时,提供部署与管理企业级 Java 应用的工具与功能。

重點摘要

支持 Jakarta EE 与 MicroProfile,社区版有 Full 与 Web 两种发行
使用 asadmin 命令管理域(start/stop/restart/create)并执行部署
示例通过 jakarta.servlet-api(provided)、WAR 打包和 HelloPayara servlet 验证部署
本文演示如何在本地下载、安装并启动 Payara Server,以及如何部署一个简单的 Jakarta EE 应用以验证服务器运行。Payara 为 GlassFish 的分支,兼容完整的 Jakarta EE 平台并支持 EJB、WebSocket、JPA 等技术,也可运行 MicroProfile 应用。社区版提供 Full Profile(完整功能)和 Web Profile(精简运行时)。Payara 附带 asadmin 命令行工具用于域的创建与生命周期管理(默认 domain1)以及应用部署。示例说明在 pom.xml 引入 jakarta.servlet-api(scope=provided)、设置 packaging 为 war、编写响应 /hello 的 HelloPayara servlet,然后运行 mvn clean package 生成 WAR。可用 ./asadmin deploy 或管理控制台(http://localhost:4848)部署。文章并讨论了 Payara 的企业功能、较大资源开销与付费企业版等权衡。
Payara 入門
Payara Server 是從 GlassFish 分支出來的完整 Jakarta EE 應用伺服器,提供管理工具與部署選項以建置與執行企業級 Java 應用。

重點摘要

支援 Jakarta EE 與 MicroProfile,社群版有 Full 與 Web 兩種發行
以 asadmin 指令管理 domain(start/stop/restart/create)並部署應用
示範建立包含 jakarta.servlet-api(provided)與 WAR 打包的 HelloPayara servlet
本文介紹如何在本機下載、安裝並啟動 Payara Server,然後部署簡單的 Jakarta EE 應用以驗證伺服器運作。Payara 為 GlassFish 的分支,支援 EJB、WebSocket、JPA 等 Jakarta EE 技術,亦可執行 MicroProfile 應用。社群版有 Full Profile(完整)與 Web Profile(精簡)可選。Payara 附帶 asadmin 指令列工具,用於啟動/停止/重啟或建立 domain(預設 domain1)、以及部署 WAR 檔。範例示範在 pom.xml 加入 jakarta.servlet-api(scope=provided)、以 packaging=war 打包、撰寫回應 /hello 的 HelloPayara servlet,並用 mvn clean package 建置。部署可透過 ./asadmin deploy 或管理主控台(http://localhost:4848)操作。文章也比較了 Payara 的完整伺服器特色與輕量伺服器的取捨。

Rename Existing Field With Elasticsearch Mapping

Rename an Existing Field in Elasticsearch via Mapping and Reindexing
Elasticsearch cannot rename fields in place; renaming requires creating a new index and reindexing documents with a transformation script.

重點摘要

Field names persist in immutable Lucene segments; mappings don't rewrite stored data.
Create a new index with the updated mapping and use _reindex with a script to move values.
Use index aliases and validate mappings/ingest pipelines to enable zero-downtime migration and rollback.
Elasticsearch does not support in-place renaming of fields because indexed documents are stored in immutable Lucene segments and mappings are descriptive rather than transformative. Updating a mapping alone won’t change existing documents, so the correct method is to create a new index with the updated field mapping and reindex existing documents using a transformation script that copies the value from the old field to the new field and removes the old key. Before reindexing, inspect index mappings, sample documents, and ingestion pipelines to avoid inconsistencies. Use index aliases to switch traffic to the new index for zero-downtime cutovers and easy rollback. Follow best practices: ensure analyzers and field settings match, run reindexing in staging, throttle or schedule during maintenance windows, and update ingestion clients to write the new field before migration to preserve data integrity.
通过 Mapping 和 Reindex 在 Elasticsearch 中重命名现有字段
Elasticsearch 无法就地重命名字段,必须新建索引并用转化脚本重新索引数据以实现字段重命名。

重點摘要

字段名存储在不可变的 Lucene segments 中,mapping 不会改写已有数据。
创建带有新字段定义的新索引,并用 _reindex 脚本将旧字段的值迁移到新字段。
使用索引别名实现零停机切换并便于回滚,同时核查 mapping 与摄取流程以避免不一致。
Elasticsearch 无法直接在原地重命名字段,因为已索引的文档位于不可变的 Lucene segments,mapping 仅描述字段属性并不会修改现有数据。正确流程是创建一个包含新字段定义的新索引,然后使用 _reindex API 并配合脚本,在复制过程中把 old_field 的值复制到 new_field 并移除旧键。操作前应检查索引 mapping、抽查文档并审查摄取管道与客户端逻辑,确保重命名后数据一致。完成重索引后通过索引别名切换以实现零停机并便于回滚。建议在预演环境执行、对齐分析器与字段设置、控制重索引负载,并在迁移前先让摄取端写入新字段以保障数据完整性。
使用 Mapping 與 Reindex 在 Elasticsearch 中重新命名欄位
Elasticsearch 無法在原地更名欄位,必須建立新索引並以轉換腳本重新索引資料來完成欄位重命名。

重點摘要

欄位名稱儲存在不可變的 Lucene segments,中途無法由 mapping 改寫。
建立帶新欄位定義的新索引,並用 _reindex 腳本將值從舊欄位搬到新欄位。
使用索引別名可達零停機切換並簡化回滾,同時檢查 mapping 與 ingest 流程以確保一致性。
Elasticsearch 無法直接在原地更名欄位,因為已索引的文件寫入不可變的 Lucene segments,且 mapping 只是描述性設定並不會改寫既有資料。正確的做法是建立一個包含新欄位定義的新索引,然後使用 _reindex 搭配腳本,在複製資料時把 old_field 的值移到 new_field 並刪除舊欄位。執行前應檢查索引 mapping、抽樣文件與 ingest pipelines,以避免不一致或遺失資料。完成 reindex 後透過索引別名切換以達到零停機並方便回滾。建議在測試環境演練、確認分析器與欄位設定相符、控制 reindex 負載,並先更新輸入流程寫入新欄位以維護資料完整性。

Mapping to String in Mapstruct

Mapping to String in MapStruct
MapStruct converts various types—primitives, enums and dates—into String representations automatically or with simple annotations like dateFormat and null-value strategies.

重點摘要

MapStruct auto-converts primitives and enums to String (e.g., int → "30", enum.name()).
Use @Mapping(dateFormat = "yyyy-MM-dd") to format LocalDate to String with DateTimeFormatter.
Handle nulls with nullValueMappingStrategy or custom @Named mapping methods for field defaults.
This tutorial demonstrates how to map different Java types to String using MapStruct. It starts with a Maven setup that adds the MapStruct dependency and JUnit for testing. Simple examples show automatic conversion of primitives (an int age to String in Person → PersonDTO) and enums (MapStruct calls enum.name() for enum→String). For dates, the @Mapping(dateFormat = "yyyy-MM-dd") attribute instructs MapStruct to use DateTimeFormatter to produce formatted strings from LocalDate. The guide also covers null handling: nullValueMappingStrategy (for returning default instances) and custom @Named mapping methods to replace null field values with defaults. Unit tests for each case verify the generated, type-safe, compile-time mapping code. Overall, MapStruct reduces boilerplate and produces predictable DTO string representations for API or UI output.
在 MapStruct 中映射为字符串
MapStruct 可自动或通过简单注解(如 dateFormat 与 nullValueMappingStrategy)将数字、枚举与日期等类型转换为字符串表示。

重點摘要

自动将基本类型与 enum 映射为字符串(例如 int → "30",enum 使用 name())。
使用 @Mapping(dateFormat = "yyyy-MM-dd") 将 LocalDate 转成指定格式的字符串。
通过 nullValueMappingStrategy 或自定义 @Named 方法处理来源或字段为 null 的情况。
本教程演示如何使用 MapStruct 将不同 Java 类型映射为字符串。以 Maven 项目示例引入 MapStruct 和 JUnit,展示基本示例:将 int 字段映射为字符串(Person → PersonDTO)以及枚举映射为字符串(MapStruct 调用 enum.name())。对于日期,使用 @Mapping 的 dateFormat 属性并借助 DateTimeFormatter 将 LocalDate 转为格式化字符串。关于空值处理,介绍 nullValueMappingStrategy(例如 RETURN_DEFAULT 返回默认目标实例)以及通过 @Named 的自定义方法为单个字段提供默认值。各例配有单元测试以验证映射结果。总体来说,MapStruct 在编译期生成类型安全的映射代码,减少样板并提高可维护性。
在 MapStruct 中映射為字串
MapStruct 可自動或透過簡單註解(如 dateFormat 與 nullValueMappingStrategy)將數值、列舉與日期等類型轉為字串表示。

重點摘要

自動將基本型別與 enum 映射為字串(例如 int → "30",enum 使用 name())。
使用 @Mapping(dateFormat = "yyyy-MM-dd") 將 LocalDate 轉為指定格式的字串。
透過 nullValueMappingStrategy 或自訂 @Named 方法處理欄位或來源為 null 的情況。
本文介紹如何在 MapStruct 中把不同資料型別映射為字串。範例專案以 Maven 與 MapStruct、JUnit 設定為起點;示範簡單的 int 到 String(Person → PersonDTO)與 enum 到 String 的自動轉換,說明 MapStruct 會呼叫 enum 的 name()。處理日期則示範使用 @Mapping 的 dateFormat 屬性,透過 DateTimeFormatter 將 LocalDate 轉為指定格式的字串。對於 null 值,說明可用 nullValueMappingStrategy 返回預設目標實例,或以 @Named 的自訂轉換方法替欄位提供預設值。每個案例均以單元測試驗證,強調 MapStruct 在編譯時產生類型安全且可測試的轉換程式碼,減少樣板程式。

Java Weekly, Issue 630

Java Weekly, Issue 630
An excellent deep dive into agentic patterns within Spring, with updates and notable releases across the Java ecosystem.

重點摘要

Spring AI agentic patterns explored in depth (clarification and task-memory fixes)
Multiple framework and tool releases (Spring, Quarkus, WildFly, Gradle, Hibernate, Elasticsearch, Vert.x, JobRunr)
Technical articles on docs-as-code, event-driven patterns, privacy, and a Pick of the Week: logical fallacies
Java Weekly Issue 630 highlights Spring and wider Java ecosystem news, focusing on Spring AI agentic patterns with two deep-dive posts about asking users for clarification and addressing agent task forgetfulness. The issue collects links to key developments such as the Jakarta Persistence 4.0 milestone, the OpenJDK January 2026 critical patch, a decade-long Java garbage collection guide, and practical pieces on functional optics, GPU optimization from Java, and avoiding JPA pitfalls with Kotlin. It lists notable releases (Spring Framework 7.0.3, Spring Security 7.1.0-M1, Quarkus, WildFly 39, Gradle 9.3.0, Hibernate ORM 7.2.1.Final, Elasticsearch 9.2.4, Vert.x, JobRunr) and upcoming webinars. The Technical & Musings section recommends further reading, and the Pick of the Week points to an essay on common logical fallacies.
Java Weekly,第 630 期
深入剖析 Spring 中的 agentic patterns,并汇总 Java 生态的重要更新与发布。

重點摘要

Spring AI 的 agentic patterns 深度解析(含向用户提问与修复代理忘记任务)
汇总多个关键框架与工具的新版本发布(如 Spring、Quarkus、WildFly、Gradle、Hibernate、Elasticsearch、Vert.x)
技术与感想类文章推荐,周荐为“8 个让人犯错的逻辑谬误”
本期 Java Weekly 侧重 Spring 与 Java 生态的最新动态,重点推荐两篇关于 Spring AI agentic patterns 的深度文章,讨论在执行前向用户澄清以及修复代理忘记任务的方法。文中还汇集了 Jakarta Persistence 4.0 里程碑、OpenJDK 2026 年 1 月关键修补、十年 Java 垃圾回收指南、功能性 optics、Java 的 GPU 优化以及 Kotlin 与 JPA 的常见陷阱等资源。列表式更新包含多项重要发布(如 Spring Framework 7.0.3、Spring Security 7.1.0-M1、Quarkus、WildFly 39、Gradle 9.3.0、Hibernate ORM 7.2.1.Final、Elasticsearch 9.2.4、Vert.x、JobRunr)并推荐相关线上讲座与技术随笔,周荐文章为关于常见逻辑谬误的讨论。
Java Weekly,第 630 期
深入探討 Spring 中的 agentic patterns,並彙整多項 Java 生態系更新與重要釋出。

重點摘要

Spring AI 的 agentic patterns 深度文章(包含向使用者發問與修復代理忘記任務)
彙整多項重要套件與平台新版釋出(如 Spring、Quarkus、WildFly、Gradle、Hibernate、Elasticsearch 等)
技術與思考類文章推薦,精選一篇為「8 個讓人犯錯的邏輯謬誤」
本期 Java Weekly 聚焦 Spring 與 Java 生態,特別推薦 Spring AI 的 agentic patterns 深度文章(包含在採取行動前向使用者提問及修復代理忘記任務的策略)。文內並彙整多篇技術與實作資源,包括 Jakarta Persistence 4.0 里程碑、OpenJDK 2026 年一月重要修補、十年垃圾回收指南、Java 上的功能性 optics、GPU 優化,以及 Kotlin 與 JPA 的常見陷阱。也列出大量套件與平台的新版釋出(如 Spring Framework、Spring Security、Quarkus、WildFly、Gradle、Hibernate、Elasticsearch、Vert.x、JobRunr)與相關線上演講,並推薦數篇技術隨筆與思考文章,精選為「8 個讓人犯錯的邏輯謬誤」。

ImmutableList vs. List in Jetpack Compose: Rethinking “Best Practice” After Strong Skipping Mode

ImmutableList vs. List in Jetpack Compose: Rethinking “Best Practice” After Strong Skipping Mode
With Strong Skipping Mode now the default, converting List<T> to ImmutableList<T> no longer guarantees better skippability and can add O(N) conversion and equality costs.

重點摘要

Strong Skipping Mode as default reduces the automatic benefit of immutable collections.
ImmutableList incurs O(N) conversion and equals() costs that can be pure overhead.
ImmutableList helps mainly when identical-content lists are frequently recreated; measure before optimizing.
Before Strong Skipping Mode became the default, using immutable collections (kotlinx ImmutableList) was recommended to improve Compose stability and skipping. Now that Strong Skipping Mode is default, converting List<T> to ImmutableList<T> doesn't automatically yield better performance in typical apps. The article compares three cases: an unchanging list instance, a list instance that only changes with content, and frequently recreated list instances with identical content. If the instance never changes, both List<T> and ImmutableList<T> act similarly. If the instance only changes when content changes, ImmutableList adds conversion and O(N) equality costs that are unnecessary since recomposition occurs anyway. If identical content is recreated often, ImmutableList can help by enabling higher-level skipping, but its conversion and equals costs may still outweigh saved work—especially with lazy composables. Conclusion: don't convert defensively; measure and optimize where real bottlenecks appear.
Jetpack Compose 中的 ImmutableList 与 List:在 Strong Skipping Mode 之后重新思考“最佳实践”
Strong Skipping Mode 成为默认后,将 List<T> 转为 ImmutableList<T> 不再自动带来更好的跳过行为,且会增加 O(N) 的转换与相等比较成本。

重點摘要

Strong Skipping Mode 作为默认,降低了不可变集合的必然优势。
ImmutableList 会产生 O(N) 的转换与 equals() 成本,可能成为额外开销。
只有在频繁重建相同内容时,ImmutableList 才可能有利;应以量测结果为准再进行优化。
在 Strong Skipping Mode 成为默认之前,Compose 常建议使用不可变集合(kotlinx ImmutableList)以提升稳定性和跳过效果。但默认模式改变后,把来自 Retrofit、Room 等的数据从 List<T> 转为 ImmutableList<T> 并不总是带来性能提升。文章以三种情形比较:实例永不变、仅在内容变更时替换实例、以及内容相同但频繁重建的实例。若实例不变,二者差异很小;若仅在内容变更时替换,ImmutableList 引入的转换与 O(N) 相等比较会成为额外开销;若频繁重建相同内容,ImmutableList 可能帮助上层跳过,但其成本有时会超过节省。结论:不必事前防御性地全部转换,应该以实际测量的瓶颈为依据再优化。
Jetpack Compose 中的 ImmutableList 與 List:在 Strong Skipping Mode 後重新思考「最佳做法」
Strong Skipping Mode 成為預設後,將 List<T> 轉為 ImmutableList<T> 不再自動帶來更好的跳過行為,且會增加 O(N) 的轉換與相等比較成本。

重點摘要

Strong Skipping Mode 成為預設,降低不可變集合的必然優勢。
ImmutableList 會產生 O(N) 的轉換與 equals() 成本,可能成為額外負擔。
只有在頻繁重建相同內容的情況下,ImmutableList 才可能有助於上層跳過;以量測結果為準再優化。
過去在 Compose 中,使用不可變集合(kotlinx ImmutableList)被視為提高穩定性與跳過的最佳做法,但 Strong Skipping Mode 成為預設後情況改變。文章比較三種常見情境:實例永不變、僅在內容變化時更換實例、以及內容相同但頻繁重建的實例。若實例不變,List<T> 與 ImmutableList<T> 行為接近;若僅在內容變更才換實例,ImmutableList 會帶來一次性轉換與 O(N) 的相等比較成本,成為純粹開銷;若頻繁重建相同內容,ImmutableList 有助於上層跳過,但其轉換與 equals 成本有時會超過節省。結論:不必事先全面轉換,應以實際量測的瓶頸為依據再優化。

How to write more stable and testable code 👉 use encapsulation

How to write more stable and testable code — use encapsulation
Encapsulation turns your domain objects into the authority over their own correctness.

重點摘要

Non-encapsulated models can be mutated directly, breaking invariants
Encapsulated objects keep state private and expose controlled operations
Tests focus on domain behavior, becoming simpler and more reliable
The article argues that encapsulation—hiding state and exposing behavior—becomes crucial when writing tests for domain logic. Using a Wallet example, it shows that a non-encapsulated design keeps business rules outside the model, which allows callers to mutate state directly and lets impossible or invalid states occur unnoticed. Tests in that design only validate correct usage by convention rather than enforcing domain invariants. By moving rules into the Wallet itself (private state, read-only views, and a single add operation), invalid inputs are rejected at the boundary, impossible states cannot be constructed, and bypassing rules is prevented. The core point is not the conditional checks but their placement: encapsulation makes domain objects authoritative about their correctness, yielding simpler tests, fewer broken tests during refactors, and consistent enforcement of invariants.
如何编写更稳定且更易测试的代码 — 使用封装
封装使你的领域对象成为自身正确性的权威。

重點摘要

未封装的模型会被直接修改,从而破坏不变式
封装将状态设为私有,暴露受控操作与只读视图
测试只需关注领域行为,更简洁且在重构后更稳健
文章以一个 Wallet(钱包)示例说明封装的重要性:在未封装的设计中,业务规则位于外部(如仓库),任意调用者可以直接修改交易列表或余额,导致不合法或不可能的状态,而测试只是在验证约定的正确使用。将规则内置到 Wallet(私有状态、只读视图与单一的添加交易操作)后,边界会拒绝无效输入,不可能状态无法被构造,规则无法被绕过。关键不在于 if 语句本身,而是它们所在的位置:封装让领域对象负责自身正确性,使测试更可信,重构时破坏测试的概率降低。
如何寫出更穩定且易測試的程式碼 — 使用封裝
封裝會使你的領域物件成為自身正確性的權威。

重點摘要

未封裝的模型易被直接變更,導致不可能狀態出現
封裝將狀態設為私有,並提供受控的操作與唯讀檢視
測試可只關注領域行為,更簡單且不易被重構破壞
作者以一個 Wallet(錢包)範例說明:若未封裝,業務規則放在外部(例如倉儲),任何呼叫者都能直接修改交易或餘額,導致不合法或不可能的狀態,但測試仍然通過,因為它們只驗證約定的正確使用。將規則移入 Wallet 本身、將狀態設為私有、暴露唯讀檢視並提供單一的領域操作後,無效輸入會在邊界被拒絕,不可能狀態無法被建立,規則也無法被繞過。重點不在於條件判斷本身,而在於它們放置的位置:封裝讓領域物件負責正確性,讓測試更可信,並降低重構破壞測試的風險。

KMP WorkManager: Enterprise-Grade Background Tasks for Kotlin Multiplatform

KMP WorkManager: Enterprise-Grade Background Tasks for Kotlin Multiplatform
A full-fledged task orchestration engine that provides a single Kotlin API for reliable background processing on both Android and iOS.

重點摘要

Cross-platform task orchestration with a pure Kotlin API
Append-only O(1) queue on iOS for high-performance persistence
Graceful shutdown/state restoration and explicit iOS exact-alarm behaviors
KMP WorkManager is a Kotlin Multiplatform task orchestration library that brings enterprise-grade, reliable background processing to both Android and iOS. It addresses platform gaps—Android’s persistent WorkManager vs. iOS’s opportunistic BGTaskScheduler—by offering a pure Kotlin API, robust state restoration, and strategies for platform limits. Key engineering choices include an append-only O(1) queue on iOS for high-performance persistence (benchmarks showing 13–40× improvements), a graceful-shutdown/savepoint mechanism that resumes multi-step chains without restarting work, and Transparent Exact Alarms with explicit iOS behaviors (show notification, attempt background run, or throw error). The library avoids Swift callbacks so shared ViewModel code requires no platform boilerplate, is production-focused (236+ tests, documentation, roadmap), is available on Maven Central (dev.brewkits:kmpworkmanager:2.1.2), and includes a demo app.
KMP WorkManager:面向 Kotlin Multiplatform 的企业级后台任务
一个完整的任务编排引擎,提供单一 Kotlin API,在 Android 与 iOS 上实现稳定可靠的后台处理。

重點摘要

跨平台且纯 Kotlin 的任务编排接口
iOS 端采用 Append-Only O(1) 队列以提升持久化性能
优雅关机与状态恢复,并对 iOS 精确闹钟提供明确处理策略
KMP WorkManager 是为 Kotlin Multiplatform 提供企业级后台任务编排的库,旨在统一 Android 与 iOS 的后台处理行为。它解决了 Android 的持久任务与 iOS 的机会主义调度之间的差异,提供纯 Kotlin 的 API、可靠的状态恢复机制和应对系统资源限制的策略。关键实现包括:iOS 持久化重写为 Append-Only O(1) 队列以显著提速(基准显示 13–40× 提升)、优雅关机与保存点使多步链可从最后成功步骤继续、以及 Transparent Exact Alarms 允许运维在 iOS 上选择显示通知、尝试后台执行或抛错。项目面向生产(236+ 测试、完整文档、示例 App),已发布于 Maven Central(dev.brewkits:kmpworkmanager:2.1.2),并在持续维护与更新。
KMP WorkManager:為 Kotlin Multiplatform 提供企業級背景工作
一個完整的任務協調引擎,提供單一 Kotlin API,在 Android 與 iOS 上實現可預期且可靠的背景處理。

重點摘要

跨平台且純 Kotlin 的任務協調 API
iOS 端採用 Append-Only O(1) 隊列以提升持久化效能
優雅關機與保存點機制,並提供 iOS 準時鬧鐘的明確處理策略
KMP WorkManager 是一個為 Kotlin Multiplatform 設計的背景任務協調庫,目標在 Android 與 iOS 間提供企業級的可靠背景處理。它針對平台差異提出解法:在 iOS 上克服 BGTaskScheduler 的機會主義限制,實作可恢復的多步工作鏈;在 iOS 持久化層使用 Append-Only O(1) 隊列以大幅提升佇列效能;加入優雅關機與保存點,確保執行中被系統終止時能從最後成功步驟繼續。此外提供 ExactAlarmIOSBehavior 三種處理策略(顯示通知、嘗試背景執行、拋錯),並以純 Kotlin API 避免 Swift 回呼。專注生產環境(236+ 測試、文件、示範 App),已在 Maven Central 發佈(dev.brewkits:kmpworkmanager:2.1.2),且持續維護與擴展。

Things That Broke My Android App Last Year – and How I Fixed Them

Things That Broke My Android App Last Year — and How I Fixed Them
Android apps rarely break because of one big bug; they fail quietly after SDK upgrades, background execution limits, OEM behavior changes, or dependency updates.

重點摘要

Background work silently stopping
Lifecycle-related leaks with Kotlin Flow
Target SDK, notification, CI and OEM power-management changes breaking behavior
Android apps seldom fail due to a single glaring bug; instead they degrade quietly when the surrounding ecosystem changes. In a practical post-mortem, the author recounts several production incidents encountered over the past year: background work that stopped without errors, lifecycle-related memory leaks when using Kotlin Flow, target SDK upgrades that altered runtime behavior, notifications failing on newer Android versions without crashes, CI builds breaking due to unmanaged dependencies, and OEM-specific power-management quirks. Rather than a rant, the write-up analyzes each failure from engineering and architecture perspectives and provides up-to-date fixes, strategies, and real code examples to diagnose and remediate the issues. The piece emphasizes defensive design—adapting to SDK changes, handling background execution limits, managing lifecycle scope, securing CI dependency management, and accounting for OEM battery optimizations—and points readers to the full Medium article for detailed code snippets and implementation guidance.
去年让我的 Android 应用崩溃的事,以及我如何修复它们
Android 应用很少因为单一严重错误崩溃;它们常在 SDK 升级、后台执行限制、OEM 行为改变或依赖更新后悄然失效。

重點摘要

后台任务静默停止
Kotlin Flow 的生命周期相关泄漏
Target SDK、通知、CI 依赖与 OEM 电源管理导致的行为变化
Android 应用通常不是由单一重大错误引起崩溃,而是在 SDK 升级、后台执行限制、OEM 行为变更或依赖项更新之后悄然失效。作者从工程与架构角度,对过去一年遇到的多起生产问题进行实务事后分析:后台工作静默停止、使用 Kotlin Flow 时的生命周期泄漏、Target SDK 升级改变运行时行为、较新 Android 版本上通知失效但不崩溃、CI 因未管理的依赖中断,以及 OEM 特有的电源管理问题。文章并非抱怨,而是提供即时的修复策略与真实代码示例,强调防御性设计与问题诊断,完整内容与代码请见 Medium 文章。
去年讓我 Android App 崩壞的事情,以及我是如何修復的
Android 應用很少因單一重大錯誤崩壞;它們通常在 SDK 升級、背景執行限制、OEM 行為改變或相依項更新後悄悄失效。

重點摘要

背景工作靜默停止
使用 Kotlin Flow 時的生命週期記憶體外洩
Target SDK、通知、CI 相依與 OEM 電源管理導致的行為改變
Android 應用通常不是因單一重大錯誤而崩潰,而是在 SDK 升級、背景執行限制、OEM 行為變更或相依項更新後悄悄失效。作者以實務事後檢討的角度回顧過去一年遇到的真實生產問題:背景工作停止、Kotlin Flow 的生命週期記憶體外洩、Target SDK 升級改變執行行為、較新 Android 版本上通知失效但無當機、CI 因未妥善管理的相依項而中斷,以及 OEM 電源管理相關問題。文章提供對應的修復策略與即時程式範例,強調從工程與架構面向預防與診斷問題,並引導讀者到 Medium 查看完整程式碼與實作細節。

Kotlin Multiplatform in 2026: Stable APIs for iOS UI Sharing

Kotlin Multiplatform in 2026: Stable APIs for iOS UI Sharing
We are no longer asking if we should share code, but how much of the iOS UI can safely be shared using the now-stable Compose Multiplatform APIs.

重點摘要

KMP now follows a three-tier approach: Shared Logic/Native UI, Hybrid UI (2026 standard), and Full Compose UI.
Skiko renders Compose directly to a CAMetalLayer enabling consistent 120Hz on ProMotion and eliminating prior stutter.
Swift Export stabilizes direct Kotlin→Swift interop (sealed classes→enums, suspend→async), removing most Objective-C glue.
By 2026 Kotlin Multiplatform (KMP) has shifted from an experimental option to a mainstream choice for teams that want shared logic without compromising native iOS fidelity. The ecosystem supports three practical tiers: keep native SwiftUI for views while sharing logic; use Compose Multiplatform for utility screens and reserve hero screens for SwiftUI (the common hybrid approach); or adopt full Compose UI enabled by Skiko’s direct Metal rendering. Swift Export’s stabilization removes Objective‑C glue, exposing Kotlin types and suspend functions as native Swift entities, simplifying workflows. Real-world cases show large code reductions (42%) and high user parity in shared UI tests, though extreme hardware-dependent components can still suffer (example: a 15% framerate drop in a trading chart). Remaining risks include opaque Compose views for XCTest and careful memory handling for large iOS objects. Tooling (Junie, Fleet, SKIE) eases migration and interop.
2026 年的 Kotlin Multiplatform:用于 iOS UI 共享的稳定 API
我们不再问是否要共享代码,而是使用如今已稳定的 Compose Multiplatform API 能安全共享多少 iOS UI。

重點摘要

KMP 发展出三层策略:共用逻辑+原生 UI、混合 UI(2026 标准)、完整 Compose UI。
Skiko 直接渲染到 CAMetalLayer,使 Compose 在 ProMotion 屏达到稳定 120Hz,消除卡顿。
Swift Export 稳定化,Kotlin 类型与 suspend 函数可直接暴露为原生 Swift,减少 Objective‑C 黏合码。
到 2026 年,Kotlin Multiplatform 已从“有趣的黑马”转为偏重共用逻辑且保留高保真 iOS 体验团队的默认架构。生态系统形成三种实际取舍:仅共用逻辑并用 SwiftUI 做视图;混合式将设置、表单等工具型界面用 Compose 共用、核心页面保留 SwiftUI(主流做法);或在 Skiko 的 Metal 渲染支持下实现全 Compose UI。Swift Export 的稳定化取消了 Objective‑C 中间层,使 Kotlin 类与 suspend 函数在 Swift 中表现为本地类型与 async,显著减少黏合工作。实作案例显示代码量下降 42%、大多数 beta 用户无法区分 Compose 与 SwiftUI 页面,但对高频硬件依赖的组件仍可能出现帧率下降(示例为交易图下降 15%)。同时需注意 Compose 在 XCTest 中呈现为不可拆分的单一视图,以及处理大型 iOS 对象时的内存管理风险。
2026 年的 Kotlin Multiplatform:可在 iOS 共用 UI 的穩定 API
我們不再問要不要共用程式碼,而是可以用現在已穩定的 Compose Multiplatform API 安全共用多少 iOS UI。

重點摘要

KMP 形成三層選擇:共用邏輯+原生 UI、混合 UI(2026 標準)、完全 Compose UI。
Skiko 直接輸出到 CAMetalLayer,讓 Compose 在 ProMotion 顯示器達到穩定 120Hz、消除卡頓。
Swift Export 穩定化,Kotlin 類型與 suspend 函式可直接作為原生 Swift 使用,減少 Objective‑C 黏著碼。
到了 2026,Kotlin Multiplatform 已從小眾選項轉為優先考慮的架構,特別適合要共用商業邏輯但維持高品質 iOS 體驗的團隊。實務上出現三種策略:僅共用邏輯、混合式將設定與表單等「工具型」畫面以 Compose 共用(大多數專案採用)、或在 Skiko 支援下整體採用 Compose。Skiko 透過直接對接 Metal 提升渲染效能,Swift Export 則消除了 Objective‑C 層,讓 Kotlin 物件與 suspend 函式在 Xcode 中像本地 Swift 模組一樣使用。案例顯示程式碼量可下降 42%、多數使用者分辨不出 Compose 與 SwiftUI 的差異,但對極度依賴硬體或專有動畫的「Hero」元件仍建議使用 SwiftUI。另外需注意 Compose 在 XCTest 中視為單一不可視化元素及大型 iOS 物件的記憶體管理。

DSA Day 3/150: Contains Duplicate (LeetCode 217)

DSA Day 3/150: Contains Duplicate (LeetCode 217)
Determine whether any value appears more than once in the input array.

重點摘要

Problem: Contains Duplicate (LeetCode 217)
Difficulty: Easy
Topics: Arrays, HashSet
LeetCode problem 217, "Contains Duplicate," asks whether any value appears at least twice in an array. Classified as Easy and tagged Arrays and HashSet, it centers on efficiently detecting repeated elements. The typical and straightforward approach iterates through the array while inserting seen values into a hash set; if an element is already present, the algorithm returns true, otherwise it finishes scanning and returns false. Using a hash set yields expected O(n) time with O(n) extra space; alternatively, sorting can detect duplicates with O(n log n) time but different space trade-offs. The brief Stackademic post lists the title, difficulty, and topic tags and links to the original problem for further reading.
DSA 第3天/150:Contains Duplicate(LeetCode 217)
判断输入数组中是否存在任意重复出现的值。

重點摘要

题目:Contains Duplicate(LeetCode 217)
难度:Easy
主题:Arrays、HashSet
LeetCode 第217题“Contains Duplicate”询问数组中是否有任一值出现至少两次。该题标注为 Easy,主题包括 Arrays 与 HashSet。常见方法是遍历数组,将已见元素加入哈希集合(HashSet),若遇到已存在的元素则返回 true,否则扫描结束返回 false。使用哈希集合在平均情况下可实现线性时间复杂度并且实现简单;也可以通过排序以不同的时间与空间折衷来检测重复。Stackademic 的文章简要列出题目标题、难度和主题标签,并提供链接以便进一步阅读。
DSA 第3天/150:Contains Duplicate(LeetCode 217)
判斷輸入陣列中是否存在任何重複出現的數值。

重點摘要

題目:Contains Duplicate(LeetCode 217)
難度:Easy
主題:Arrays、HashSet
LeetCode 第217題「Contains Duplicate」詢問陣列中是否有任一數值出現至少兩次。題目標示為 Easy,主題標籤包含 Arrays 與 HashSet。常見解法是遍歷陣列,將已見元素加入雜湊集合(HashSet),若遇到已存在的元素則回傳 true,否則掃描結束後回傳 false。此雜湊集合方法在平均情況下可達線性時間複雜度且實作簡明;也可透過排序以不同的時間與空間權衡來檢測重複。Stackademic 的文章簡短列出題名、難度與主題標籤,並提供連結以便深入閱讀。

Recomposition 101: How the Compose Compiler Plugin Tracks State Changes

Recomposition 101: How the Compose Compiler Plugin Tracks State Changes
Unlocking the mysteries of the Slot Table, positional memoization, and the hidden bytecode that powers your UI.

重點摘要

Explains the Slot Table's role in tracking composable state
Describes positional memoization to minimize recomposition
Reveals how compiler-generated bytecode records and compares state
This article breaks down how the Jetpack Compose compiler plugin detects and minimizes UI recomposition by leveraging the Slot Table, positional memoization, and generated bytecode. It outlines the Slot Table as the runtime structure that records composable calls and their associated state, explains how positional memoization lets the framework avoid unnecessary work by matching call positions rather than identities, and shows that the compiler emits bytecode to read, write, and compare stored values at runtime. The piece emphasizes why these mechanisms matter for UI performance and predictability, and guides readers through the conceptual flow from source code annotations to the low-level operations that drive efficient updates in Compose-based interfaces.
Recomposition 101:Compose 编译器插件如何追踪状态变化
解开 Slot Table、位置化记忆(positional memoization)与支撑界面运行的隐藏字节码之谜。

重點摘要

Slot Table 是记录 composable 调用与状态的运行结构
位置化记忆通过位置匹配减少不必要的重新组合
编译器生成的字节码用于读取、写入并比较存储值以追踪变化
本文拆解了 Jetpack Compose 编译器插件如何借助 Slot Table、位置化记忆和自动生成的字节码来检测并减少 UI 的重新组合。文章介绍了 Slot Table 在运行时如何记录 composable 的调用顺序与关联状态,解释了位置化记忆如何通过基于位置的匹配避免额外工作,并指出编译器会生成字节码以在运行时读取、写入和比较这些值。作者强调这些机制对界面性能和可预测性的影响,并阐述了从源码标注到低层操作驱动高效更新的概念流程。
Recomposition 101:Compose 編譯器外掛如何追蹤狀態變更
解開 Slot Table、位置化記憶(positional memoization)與支撐介面運作的隱藏位元組碼之謎。

重點摘要

Slot Table 作為記錄 composable 呼叫與狀態的執行結構
位置化記憶透過位置匹配來避免不必要的重新組合
編譯器產生的位元組碼讀寫並比較儲存的值以追蹤變化
本文解析 Compose 編譯器外掛如何透過 Slot Table、位置化記憶和自動產生的位元組碼來偵測與降低 UI 的重新組合成本。文章說明 Slot Table 在執行期間如何紀錄 composable 的呼叫位置與相關狀態,說明位置化記憶透過依據呼叫位置比對狀態來避免不必要的工作,並指出編譯器會輸出用以讀取、寫入與比較這些狀態的位元組碼。作者強調這些機制對 UI 效能與可預測性的重要性,並說明從原始程式碼到低階操作之間的概念流程。

Kotlin Collections: Mastering associate, associateBy, and associateWith

Kotlin Collections: Mastering associate, associateBy, and associateWith
As Kotlin developers, we deal with Collections every single day.

重點摘要

Overview of associate, associateBy, and associateWith
How each function transforms collections into maps
Practical usage patterns and common pitfalls
This article focuses on three Kotlin collection extension functions—associate, associateBy, and associateWith—and how they help transform collections into maps. It explains the roles of each function: associate builds a map by mapping elements to key-value pairs produced by a transform, associateBy creates a map keyed by a selector function (with elements or transformed values as values), and associateWith builds a map from each element to a computed value. The piece compares their typical use cases, discusses behavior with duplicate keys (last value wins) and nullability considerations, and highlights when to prefer one function over another for clearer, more idiomatic code. Practical examples and tips illustrate how to convert lists and sequences into maps efficiently and readably for everyday Kotlin development.
Kotlin 集合:精通 associate、associateBy 与 associateWith
作为 Kotlin 开发者,我们每天都在处理 Collections。

重點摘要

介绍 associate、associateBy 与 associateWith 的用途
说明三者如何将集合转换为 Map
实用示例、要点比较与常见陷阱
本文聚焦 Kotlin 的三种集合扩展函数:associate、associateBy 与 associateWith,讲解它们在将集合转换为 Map 时的用途与差异。associate 通过转换函数为每个元素生成键值对;associateBy 使用选择器函数作为键,值为元素或转换后的结果;associateWith 将每个元素映射到一个计算出的值。文章比较典型使用场景,讨论重复键(以最后一个值生效)和可空性等须注意的问题,並提供实用建议与示例,帮助开发者在日常开发中更清晰、惯用地把 List 或 Sequence 等集合转换为 Map。
Kotlin 集合:精通 associate、associateBy 與 associateWith
作為 Kotlin 開發者,我們每天都在處理 Collections。

重點摘要

介紹 associate、associateBy 與 associateWith 的用途
說明三者如何將集合轉換為 Map
實用範例、重點比較與常見陷阱
本文聚焦於 Kotlin 常用的三個集合擴充函式:associate、associateBy 與 associateWith,說明它們在將集合轉為 Map 時的不同角色與用法。associate 透過轉換函式為每個元素產生鍵值對;associateBy 以選取器函式作為鍵,值為元素本身或轉換後結果;associateWith 則將每個元素對應到一個計算出的值。文章比較典型情境、重點差異及重複鍵(以最後一個值為準)與 null 處理等注意事項,並以實例與實務建議示範如何用更簡潔且具表意性的方式在日常開發中將 List/Sequence 等集合轉為 Map。

Episode 18: Kotlin, the Language That Politely Fixes Java Without Starting a Family Argument

Episode 18: Kotlin, the Language That Politely Fixes Java Without Starting a Family Argument
Some languages arrive announcing they'll rewrite everything from scratch; Kotlin prefers to politely fix Java without causing a family argument.

重點摘要

Contrasts bombastic rewrites with Kotlin’s restrained approach
Kotlin improves Java interoperability and ergonomics
Emphasizes gradual, non-disruptive adoption
Dave LumAI’s Episode 18 contrasts bombastic language design with Kotlin’s restrained approach. Rather than arriving 'in a leather jacket' and proclaiming a total rewrite, the piece frames Kotlin as a language that addresses Java’s shortcomings without forcing disruptive change. It highlights Kotlin’s pragmatic interoperability, concise syntax, and gradual adoption path that lets existing Java codebases coexist with newer Kotlin modules. The article uses a family-argument metaphor to stress that improvements can be introduced politely—by offering safer defaults, clearer constructs, and tooling that integrates with established workflows—rather than by demanding immediate replacement. For teams and maintainers, Kotlin is presented as a practical alternative that reduces migration friction while delivering modern language ergonomics, making it possible to evolve a codebase incrementally instead of triggering a divisive overhaul.
第18集:Kotlin——礼貌地修补 Java 而不引发家庭争吵的语言
有些语言像穿着皮夹克登场,宣称要把一切重写;Kotlin 则选择礼貌地修补 Java,而不引发家庭式的争论。

重點摘要

对比激进重写与谨慎改进的风格
强调与 Java 的互操作性与共存能力
主张渐进采用,降低迁移冲突
Dave LumAI 在第18集中用比喻对比了不同编程语言的姿态:有些语言夸张地宣称要彻底重写一切,而文章把 Kotlin 描绘为一种更克制的选择,旨在修补 Java 的不足而不迫使大刀阔斧的替换。文中强调 Kotlin 与既有 Java 生态的互通性、更简洁的语法与可逐步采用的路径,指出通过更安全的默认设置、清晰的语言构造和兼容的工具链,团队可以在不引发剧烈争论的前提下逐步改进代码库。
第18集:Kotlin——禮貌地修補 Java 而不引發家庭爭吵的語言
有些語言像穿著皮衣登場,宣稱要把一切從頭重寫;Kotlin 則選擇禮貌地修補 Java,而不挑起家庭式的爭論。

重點摘要

對比激進重寫與謹慎改進的做法
強調與 Java 的相容性與可共存性
主張漸進採用、減少遷移衝突
Dave LumAI 在第18集中以比喻呈現語言設計風格:有的語言像穿皮衣登場、宣稱要全面重寫;文章則把 Kotlin 描繪成一個溫和修補 Java 的語言,強調與現有 Java 生態互通、語法更簡潔、可逐步採用而不造成劇烈衝突。作者指出,透過提供更安全的預設、清晰的語言結構與相容工具,Kotlin 能在不挑起「家庭爭吵」的情況下改良代碼庫,使團隊得以漸進演進而非全面翻新。

From Scala to Kotlin: A Frustrated Engineer’s Honest Take

From Scala to Kotlin: A Frustrated Engineer’s Honest Take
I'm frustrated and emotional, but convinced these feelings are grounded in real technical and cultural shifts.

重點摘要

The author expresses frustration and strong emotions.
He believes those emotions stem from real technical and cultural shifts.
The topic centers on moving from Scala to Kotlin and its impacts.
In "From Scala to Kotlin: A Frustrated Engineer’s Honest Take," Seetaramayya Vadali admits he feels frustrated and emotional about the transition from Scala to Kotlin. He underscores that his emotions are not merely personal reactions but are rooted in tangible technical and cultural changes affecting engineers and teams. The excerpt frames the piece as a candid, personal perspective that links human feelings to concrete shifts—such as tooling, ecosystem expectations, and team practices—without detailing specific grievances in the short excerpt. The author invites readers to consider both the emotional and technical dimensions of language migration, presenting an honest account that aims to explain why the switch matters on multiple levels and pointing readers to the full Medium post for the complete argument.
从 Scala 到 Kotlin:一位沮丧工程师的真诚看法
我感到沮丧且情绪化,但相信这些情绪基于真实的技术与文化变迁。

重點摘要

作者承认自己感到沮丧和情绪化。
他认为这些情绪源自真实的技术与文化变化。
文章关注从 Scala 迁移到 Kotlin 的影响。
在《From Scala to Kotlin: A Frustrated Engineer’s Honest Take》中,作者 Seetaramayya Vadali 坦言对从 Scala 迁移到 Kotlin 感到沮丧且情绪化,但强调这些情绪并非无端,而是扎根于真实的技术与文化变迁。摘录呈現為一段誠懇的個人觀察,指出語言遷移牽涉到工具、生態與團隊實務等具體面向,這些改變會轉化為工程師的情緒反應。作者希望讀者同時關注人性面與技術面,並在 Medium 連結中閱讀完整論述。
從 Scala 到 Kotlin:一位挫折工程師的誠實看法
我感到挫折且情緒化,但相信這些情緒是基於真實的技術與文化轉變。

重點摘要

作者坦承自己感到挫折與情緒化。
他認為這些情緒根植於真實的技術與文化變化。
討論主題為從 Scala 轉向 Kotlin 所帶來的影響。
作者在〈From Scala to Kotlin: A Frustrated Engineer’s Honest Take〉中坦承對從 Scala 轉向 Kotlin 感到挫折與情緒化,但強調這些情緒並非無的放矢,而是紮根於真實的技術與文化轉變。文章以個人體驗為出發,指出語言遷移會牽涉到工具、團隊運作與生態系預期等具體面向,這些變化會影響工程師的感受。作者以誠實、情感化的口吻,試圖讓讀者理解語言轉換不僅是技術選擇,也包含人性的層面;完整論述可見原文 Medium 連結。

Kotlin Collections: Mastering associate and associateTo

Kotlin Collections: Mastering associate and associateTo
The Kotlin Standard Library includes utility functions such as associate and associateTo to convert collections into maps efficiently.

重點摘要

associate and associateTo are built-in Kotlin collection utilities
They transform collections into maps using selector/transform functions
associate returns a new Map while associateTo populates a given mutable Map
This article examines two Kotlin Standard Library utilities—associate and associateTo—and how they help convert collections into maps. It explains their roles, differences, and typical use cases: associate produces a new Map from a collection using key/value selectors, while associateTo writes entries into an existing mutable Map, which can avoid allocations when reusing a destination. The piece highlights when to prefer one over the other, common patterns for transforming collection elements into map entries, and practical tips for writing concise, idiomatic Kotlin using these functions. Readers gain a clearer understanding of how to build maps from collections and when to reuse a destination map for performance or code-structure reasons.
Kotlin 集合:掌握 associate 与 associateTo
Kotlin 标准库包含 associate 与 associateTo 等实用函数,可将集合高效地转换为映射(Map)。

重點摘要

associate 与 associateTo 为 Kotlin 内置集合工具
通过选择或转换函数将集合元素转为 Map 条目
associate 返回新的 Map,associateTo 则填充到已有的可变 Map
本文介绍 Kotlin 标准库中将集合转换为映射的两个实用函数:associate 与 associateTo,解析它们的作用与差异。associate 根据键值选择器从集合生成并返回新的 Map;associateTo 则把条目写入传入的可变 Map,适合复用目标以减少分配开销。文章讨论何时使用各自函数、常见的转换模式,以及在写出简洁且惯用的 Kotlin 代码时可参考的实践与效能考量,帮助读者更明确地将集合构造成 Map。
Kotlin 集合:精通 associate 與 associateTo
Kotlin 標準函式庫提供像 associate 與 associateTo 這類工具函數,可用來將集合有效地轉換為映射(Map)。

重點摘要

associate 與 associateTo 為 Kotlin 內建集合工具
透過選取或轉換函數將集合元素轉為 Map 條目
associate 回傳新的 Map,associateTo 則填入既有的可變 Map
本文介紹 Kotlin 標準函式庫中兩個用於集合到映射轉換的工具函數:associate 與 associateTo。說明兩者的用途與差異:associate 依據鍵值選取器從集合建立並回傳新的 Map;associateTo 則將條目寫入傳入的可變 Map,適合重複使用目的地以降低配置成本。文章並說明常見使用情境與模式、何時選擇重用目的地以提升效能,以及如何以簡潔且慣用的 Kotlin 寫出集合轉 Map 的程式碼。

Roadmap for Android (Kotlin): Junior → Mid → Senior level,

Roadmap for Android (Kotlin): Junior → Mid → Senior level
A clear, practical roadmap of Android (Kotlin) topics, split for Junior → Mid → Senior level—what companies actually expect.

重點摘要

Practical Kotlin Android roadmap
Split into Junior → Mid → Senior progression
Aligned with employers' real expectations
This short article presents a clear, practical roadmap for Android development using Kotlin, organized into three progression levels: Junior, Mid, and Senior. It focuses on the topics and skills employers typically expect at each stage, helping developers prioritize what to learn as they advance. Intended as a concise guide rather than exhaustive documentation, the roadmap is useful for planning study, preparing for interviews, and aligning personal growth with industry needs. Readers can use it to identify gaps in their knowledge, set milestones, and communicate their competencies to hiring teams. Although brief, the piece aggregates realistic workplace expectations into actionable learning milestones, making it easier for learners to map effort to career targets and to build a coherent progression that hiring managers recognize.
Android(Kotlin)学习路线:初级→中级→高级
一份清晰且实用的 Android(Kotlin)路线图,按初级→中级→高级划分主题,反映企业真实期待。

重點摘要

实用的 Kotlin Android 路线图
按初级→中级→高级分层
与企业招聘期望相匹配
这篇短文提供一份清晰且实用的 Android(Kotlin)学习路线,按初级→中级→高级三层拆分主题,指出企业在每个阶段实际期待的技能。文章以实务为导向,帮助开发者确定学习优先级、规划进阶目标,并可用于面试准备或与雇主对齐职业发展。它并非详尽教程,而是将职场需求转化为可执行的学习里程碑,便于检视能力差距、建立作品集并展示成长轨迹。
Android(Kotlin)學習路線:初階→中階→高階
一份清晰且實用的 Android(Kotlin)路線圖,依初階→中階→高階拆分主題,呈現企業實際期待。

重點摘要

實用的 Kotlin Android 路線圖
依初階→中階→高階分級安排
對齊企業真實的職場期待
這篇短文提供一份清晰且實用的 Android(Kotlin)學習路線,依初階→中階→高階三個等級拆分主題,指出企業在各階段實際期待的技能。文章以實務導向為重點,協助開發者排序學習優先順序、規劃進階目標,並可用於準備面試或與雇主對齊職涯發展。它不是詳盡教學,而是將職場需求轉化為可執行的學習里程碑,便於檢視能力缺口、建立作品並呈現成長軌跡。

Unmasking Hidden Memory Leaks: The Silent Performance Killer in Android Apps

Unmasking Hidden Memory Leaks: The Silent Performance Killer in Android Apps
Hidden memory leaks in Android apps quietly consume resources and cause slowdowns you may never notice until performance degrades significantly.

重點摘要

Memory leaks silently accumulate and degrade app performance over time
Leaks are often caused by retained references, lifecycle mismanagement, or background tasks
Detection and prevention require profiling, targeted testing, and best-practice coding
Hidden memory leaks in Android applications are a common but often invisible cause of progressive slowdowns and instability. Because leaks can accumulate gradually or only trigger under specific conditions, they may evade casual testing and appear as unexplained performance regressions in production. Typical causes include accidentally retained Contexts or Views, static references, unremoved listeners or callbacks, and background tasks that outlive their intended scope. Effective mitigation relies on systematic detection with tools like profilers and LeakCanary, disciplined lifecycle management, avoiding long-lived strong references to UI or Context objects, and thorough testing under realistic workloads. Proactive monitoring, regular memory profiling, and adhering to memory-safe patterns during development help unmask and eliminate these silent performance killers before they affect end users.
揭开隐蔽的内存泄漏:Android 应用中的无声性能杀手
隐蔽的内存泄漏会悄然吞噬资源,使应用性能逐步下降而不易察觉。

重點摘要

内存泄漏会累积并逐渐削弱应用性能
常见原因有保留不应存在的引用、生命周期管理不当和后台任务存活过久
借助分析工具、规范生命周期和充分测试可发现并防止泄漏
隐蔽的内存泄漏是 Android 应用中常见但难以直接观察的性能问题,会随着时间消耗更多内存,从而引发运行缓慢或不稳定。由于某些泄漏仅在特定条件或长期运行后显现,常规测试可能察觉不到。典型原因包括错误保留的 Context 或 View、静态引用持有 UI 对象、未注销的监听器或回调,以及超出预期生命周期的后台任务。有效应对需要使用内存分析工具(如 Android Profiler、LeakCanary 等)、严格管理组件生命周期、避免对 UI/Context 的长寿命强引用,并在逼近真实使用场景下进行持续监控与测试,从而在影响用户之前发现并修复泄漏。
揭露隱藏的記憶體洩漏:Android 應用中的沉默效能殺手
隱藏的記憶體洩漏會悄悄耗盡資源,導致效能逐步下降而不易察覺。

重點摘要

記憶體洩漏會累積並逐漸拖慢應用效能
常見原因包括持有不該保留的參考、生命週期管理不當與背景任務
透過分析工具、生命週期規範與測試可發現並預防洩漏
隱藏的記憶體洩漏是 Android 應用中常見但不易察覺的效能問題,會隨時間累積資源佔用,導致應用變慢或不穩定。由於某些洩漏只在特定情況或長時間運行後顯現,簡單測試常難以發現。典型成因包括錯誤保留的 Context 或 View、靜態變數持有 UI 物件、未移除的監聽器或回調,以及生命週期外仍在執行的背景工作。要有效處理這類問題,需要使用記憶體分析工具(如 Android Profiler、LeakCanary)、嚴謹管理元件生命週期、避免長期強參考 UI/Context,並在真實負載下進行系統性測試與監控,以在問題影響使用者前找出並修正洩漏。

Stop Letting ViewModels Decide: The Day I Added a Use Case Layer

Stop Letting ViewModels Decide: The Day I Added a Use Case Layer
I added a use case layer to stop ViewModels from making decisions and to keep UI logic focused on state and presentation.

重點摘要

Introduced a use-case layer to remove decision-making from ViewModels
Aimed to improve separation of concerns and testability
Part of the StreamBox branch-by-branch series; full post on Medium
In "Stop Letting ViewModels Decide: The Day I Added a Use Case Layer," Manish Dubey describes introducing a use-case layer so ViewModels no longer make business decisions. The post, part of the StreamBox branch-by-branch series, presents the change as a focused refactor: extracting business logic and decision-making into dedicated use-case classes while leaving ViewModels to manage UI state and presentation. The author frames the adjustment as a practical improvement for code clarity, separation of concerns, and easier testing. Written as a concise case study, the article outlines the author’s motivations and the practical direction of the refactor and points readers to the full Medium post for branch-level implementation details.
别再让 ViewModel 决定:我添加 Use Case 层的那一天
我在项目中加入 use‑case 层,把决策和业务逻辑从 ViewModel 中抽离,使其只负责 UI 状态与呈现。

重點摘要

新增 use‑case 层以防止 ViewModel 做出决策
改善職责分离并提升可测试性
该文属于 StreamBox branch‑by‑branch 系列,完整内容见 Medium 原文
本文由 Manish Dubey 撰写,记录作者在项目中新增 use‑case 层的经历,目的在于不再让 ViewModel 承担决策与业务逻辑。作为 StreamBox branch‑by‑branch 系列的一篇文章,作者描述将决策逻辑抽离到独立的 use‑case 类中,使 ViewModel 专注于 UI 状态与表现。文章以实务重构为核心,说明此做法如何带来更清晰的职责划分与更容易的测试,并指向 Medium 原文以获取分支级的实现细节。
別再讓 ViewModel 決定:我新增 Use Case 層的那一天
我在專案中加入 use‑case 層,將決策與業務邏輯從 ViewModel 抽離,讓它們只負責 UI 狀態與呈現。

重點摘要

新增 use‑case 層以避免 ViewModel 做出決策
強化職責分離並提升可測試性
此文屬 StreamBox branch‑by‑branch 系列,詳情請見 Medium 原文
本文由 Manish Dubey 撰寫,記述作者在某一天於專案中新增 use‑case 層的經驗,目的是避免把決策與業務邏輯放在 ViewModel 裡。文章屬於 StreamBox branch‑by‑branch 系列,說明如何將具體行為與決策封裝到獨立的 use‑case 類別,讓 ViewModel 專注於管理 UI 狀態與呈現。作者將此變動視為實務上的重構,帶來更清晰的職責分工與更容易的測試流程,並引導讀者到 Medium 原文以取得分支層級的實作細節。

Generated by Kotlin News Summarizer with AI Summaries