本系列 Lesson 1~Lesson 16 的程式碼,可以在 GitHub 取得。
針對 Windows、Linux 平台,已編譯並建立可直接執行的檔案,可於 Release 頁面 取得。
lesson15-binary-linux.tar.gzlesson15-binary-windows.ziplesson16-binary-linux.tar.gzlesson16-binary-windows.zip
其他 JAR 檔案是 ASDF 打包後的結果,可以參考將 ASDF 打包成 JAR了解如何載入並執行。不過,只有後面的 Lesson 正確調整了資源位置;最早的 Lesson 可能無法正確載入 CSS 和 ICON,建議直接使用倉庫中的原始碼。
本系列之後也預計發表於個人網站:https://www.lagagain.com/
請多關照~
這篇後記回顧了系列的發想、寫作過程與完賽後對 JavaFX 的看法,也分享了 AI 如何協助寫作、又有哪些地方仍需自己查證與重寫。對我而言,參賽最大的收穫不只是學習 JavaFX,也包括透過持續輸出建立學習、寫作與時間管理的習慣;文末另補充幾則沒有納入主線的 JavaFX 和 ABCL 筆記。
這次直到最早的開賽日過後,我才開始構思系列內容(最早可於 8 月 1 日開賽,我則到 8 月 26 日才開始規劃)。相較之下,前幾年參賽時基本上都已有規劃,像是30天 Lua重拾筆記從當年 3 月開始構思;用Keycloak學習身份驗證與授權在 1、2 月就有了想法;這些那些你可能不知道我不知道的Web技術細節和與雲原生精靈共舞:APISIX使用者的兩年旅程則基本上與工作相關。但這次不同,主題幾乎完全出於個人喜好,更有種回到最初參加鐵人賽時,邊寫邊學習的感覺。
這次的發想來自 6~7 年前的筆記和 gist,也有一部分來自為什麼選擇 JavaFX提到的:我有點受夠了越來越多畫面看似華麗,用起來卻極度不成熟的 Electron 應用。
學習主題的發想畢竟來自 6~7 年前的筆記,不一定真的適合自己,因此我也透過 AI 調查了幾個有興趣的候選項目。以下是 ChatGPT 提供的總結:
如果單純以 2026 年「新專案值得選擇」來看,我會大致排:
Qt / Avalonia > wxWidgets > GTK > JavaFX
但如果限定 Linux Desktop:
GTK / Qt > wxWidgets / Avalonia > JavaFX
而如果你是在做跨平台企業內部工具,我會特別推薦你把 Qt、Avalonia、wxWidgets 三個拿來深入比較;JavaFX 和 GTK 的適用情境相對更明確。
我也詢問了幾種介面描述語言的設計差異:
| 特性 | QML | FXML | AXAML |
|---|---|---|---|
| 所屬框架 | Qt Quick | JavaFX | Avalonia UI |
| UI 宣告式語法 | 有 | 有(XML) | 有(XAML) |
| 可直接寫程式邏輯 | 可以 | 不行(純 XML) | 有限度可以 |
| 內嵌 Script 語言 | JavaScript | 無 | 無原生通用 Script |
| 事件處理 | JS 函式、繫結 | 指向 Java Controller 方法 | Event Handler、Command |
| 複雜邏輯 | QML + C++/Python 等 | Java Controller | C# |
| UI 與邏輯分離 | 可選擇分離 | 常見做法 | 常見做法 |
以我的經驗,使用 AI 查詢確實輕鬆很多,但不可全信。AI 生成的結果仍有可能不正確,例如 JavaFX 的 FXML 也可以寫 Script,提供了 <fx:script/> 標籤。我對 Avalonia UI 不熟(但有興趣),另一個 AI 卻告訴我,AXAML 沒有在介面描述語言中直接加入程式邏輯的設計,與上述是衝突的。
不只這次如此。我詢問 Grafana 設定問題,特別是 Infinity 和 UQL 的用法時,也經常得到無法直接使用的結果。自己閱讀文件和除錯的能力仍然不可或缺。
這次雖然是先有程式碼、後有文章,但每個階段的程式碼都有明確的學習目標。
完成一個學習目標後,除了已規劃的下一個目標,經常也會自然長出次要的分支目標,因此規劃內容並不太困難。
不過,真的要把內容寫成文章,經常又像是另一回事。AI 是個好工具,這次寫作也多少有 AI 輔助;依使用程度,大致可以分成三個層次:一、少量使用 AI,糾正錯字、協助總結,或多生成一些輔助圖片;二、提供大綱,由 AI 生成完整文章;三、只提供主題方向,由 AI 自行查找資料並生成完整文章。
以這次經驗來說,輕度使用 AI 的幫助很大!
錯字更正省去了大量心力檢查,甚至有一次的經驗是自己記錯程式碼作用,重新檢視後發現AI的觀點才是正確的。
生成的圖片可以很好看,儘管能夠感覺就是生成的(也僅有一張相對樸素的感覺沒那麼明顯),但因為並不是系列重點。
先提供內容大綱,再由 AI 生成文章內容;大概有兩篇是這麼產生的。
但看似很輕鬆,有一篇生成了三次才是相對滿意的內容。並且內容上也幾乎有七成重新改過。
可能我的參賽目的有關,並沒有感受到輕鬆多少。
大概有四、五篇原本只提供主題方向,最多再附上程式碼範例,讓 AI 自行推論意圖、查找資料,再參照其他篇文章的風格生成內容。
但最終,這種做法產生的內容幾乎都被我重寫過。並不是生成的內容不好,後續調整時我也參考了其中一些內容;只是重寫之後,我才發現自己不喜歡原本的表達。原因之一,或許是它在邏輯描述、說明方式和內容順序上都與我的思路不合,讀起來像是在吞刺,反而讓我不舒服。
使用 AI 生成內容還有一個問題:只用簡單的提示詞產生的內容,容易充滿 AI 味。這種味道已經氾濫到令人反感。我也曾詢問一篇文章更深層的細節與意圖,提供內容的人卻完全答不上來,實在稱不上作者。TechNews 科技新報的一篇報導提到,AI 研究實驗室 Exa 的一位員工如此說明這個問題:「如果連寫作都不願花時間,別人又為何要花時間閱讀?」
這次參加 iT 鐵人賽,最大的目的之一是學習 JavaFX。因此,「如果是為了輸入而做輸出的話,不應該太依賴AI」。資料轉化為資訊的過程分成三個步驟:一、輸入;二、處理;三、輸出。我們可以透過 AI 產生輸出,也可以把 AI 產生的內容當作輸入;但學習中最重要的「處理」部分,AI 很難取代。百靈果 News在 09 月 10 日發布的 Podcast 中訪問了洪兆祥博士。洪博士提到:「在問 AI 之前,先自己想一次。」我想再補充一點:「在問 AI 之前,先自己想過至少一次答案。」
「透過輸出整理學習輸入」的想法,與前一年〈AI 都能寫了,為何我們還要「練鐵人」?〉相同。iT 鐵人賽不只是分享概念或技術本身;這次參賽也帶來意料之外的收穫,例如時間管理、習慣養成和學習方法。關於學習,前面已經提過一些;這次我對學習方法也有更大的改變和收穫,只是很難與大家分享。簡單來說,就是「一口一口吃掉大象」(How do you eat an elephant? One bite at a time!)。在 30 天裡持續思考、學習、寫作,養成輸出的習慣,也學著分配時間與精力。這些都是參加 iT 鐵人賽能額外體驗和獲得的收穫。
技術變化很快,JavaFX 以外的跨平台 GUI 選擇提到了不少替代品。在行動裝置普及、智慧穿戴裝置和智慧家電日益增加的環境下,需要考量的也不再只是桌面應用。
Jie_Chung 今年在 COSCUP 分享的主題「編輯器的歷史、現況與未來」提到了使用 Rust 開發的 GPUI 框架。我相信這樣的後起之秀還有很多。
不過,JavaFX 也並非止步不前。年初發布的 JavaFX 27 改善了 macOS 上的渲染管線效能,也加入許多新特性、修正問題並更新組件版本。(注意:這可能和 Java 27 一樣,並非長期支援版本(LTS)。)
此外,JavaFX 背後有 Gluon 和 Oracle 支持;是否有可靠的商業支援,也經常是商業應用需要考量的因素。
JavaFX 不一定是跨平台桌面應用開發的最佳選項,但已經經過時間考驗,是值得考慮的選擇。
有時間我應該也還是會去玩玩Avalonia就是XD
JavaFX 也有 WebKit 核心和 WebView 組件,應該可以做到類似 Electron.js 或 Tauri 的用法。Java 8 文件中有相關範例 WebViewSample,我也用 JavaFX 25 編譯並確認可以運行。我還找到 coldnew's blog 上的文章使用 Clojure 和 JavaFX WebView 來打造桌面程式,原本也想用 ABCL 做類似的事情。
Gluon 除了提供 JavaFX SDK 和 Scene Builder 外,還有一個 Scene Builder Kit。我沒有使用過;依據〈Scene Builder Kit深度解析〉,似乎可以將 Scene Builder 直接嵌入自己的 JavaFX 應用中。
ABCL 的 REPL 環境同時也是一個好用的除錯器,前提是得先學會使用。
進入 REPL 環境後,通常會看到提示:輸入 :help 即可檢視說明文件。
CL-USER(1): :help
COMMAND ABBR DESCRIPTION
apropos ap apropos
bt backtrace n stack frames (default 8)
cd change default directory
cf compile file(s)
cload cl compile and load file(s)
continue cont invoke restart n
describe de describe an object
error err print the current error message
exit ex exit lisp
frame fr set the value of cl:* to be frame n (default 0)
help he print this help
inspect in inspect an object
istep i navigate within inspection of an object
ld load a file
ls list directory
macroexpand ma macroexpand an expression
package pa change *PACKAGE*
pwd pw print current directory
reset res return to top level
rq require a module
trace tr trace function(s)
untrace untr untrace function(s)
Commands must be prefixed by the command character, which is ':' by default.
以下是幾個常用功能:
:apropos <keyword>:查詢包含keyword的符號:describe <value>:查看值的描述文檔:inspect <value>:檢視<value>
特別是經常在除錯的時候使用:
:error:顯示當前的錯誤訊息:bt <num>:顯示<num>層呼叫堆疊:frame <num>:提取<num>層frame資訊我一度找不到存取除錯環境中區域變數的方法,詢問 AI 也沒有得到答案。其實可以用最後一個指令;例如,先提取第 8 層 frame 的資訊:
:fr 8
接著可以將指令的返回值(*)賦給特定變數:
(setf *frame* *)
Java 路徑系統有 file:、jar:file:...! 等形式,也區分相對路徑與絕對路徑。Common Lisp 的 pathname 則有 logical-pathname 和 truename。ABCL 擴充了 pathname,新增 ext:url-pathname 和 ext:jar-pathname。
cl:pathname (基礎檔案路徑)
├── cl:logical-pathname (邏輯路徑)
└── ext:url-pathname (網路 URL 路徑)
└── ext:jar-pathname (Java JAR 內部的檔案路徑)
這套設計感覺有點複雜。雖然也有 #'uri-encode 和 #'uri-decode,但我用過後覺得不太順手,因此最後沒有另外寫成一篇。
發揮 ABCL 威力建立了一個能在 JavaFX 應用中評估 LISP 表達式的工具,並在更多的 Dialog 類型中用它進行更多探索。
為了方便探索 JavaFX,在後續的使用上,並不是只有在該篇提到的檢查是否有輸入,還使用#'restart-case包裹#'eval來處理例外狀況:
(restart-case (eval (read-from-string _forms))
(continue nil nil)
(eval-other-form (other-form)
:interactive (lambda nil
(format t "Other Form: ")
(force-output)
(list (read)))
(eval other-form))
(return-value (new-value)
:interactive (lambda nil
(format t "Return Value: ")
(force-output)
(list (read-line)))
new-value))
當輸入的 form 有問題時,這會進入終端機的互動模式,讓我選擇忽略錯誤並直接返回 nil或提供的返回值;也可以選擇換一個 form 重新評估。
Common Lisp 的狀況處理(錯誤處理)很特別;在其他語言中,我幾乎沒見過同等或相近的設計,有些甚至在語言設計層面上,我認為完全不可能實現。我之前寫過一篇相關的快速筆記,一度想再多寫幾篇說明,但最後還是把重心稍微移向 JavaFX。
本系列分成四個部分(Session):
第一部分(Session 1)旨在初步認識並準備環境。
第二部分(Session 2)是主要內容,從 HelloFX 開始,介紹第一個互動元件、資源存取、CSS、FXML,再由 ABCL 回到 Java,並打包、分發應用。
第三部分(Session 3)和第四部分(Session 4)則補充更多 JavaFX 與 ABCL 的內容。
系列至此告一段落。對我而言,最大的收穫不只是學習 JavaFX,也是在持續輸出中培養學習與寫作的習慣。希望你也能從中有所收穫。