Hacktoberfest 2026:维护者为十月标记出来的 issue,仍然开放、适合新手。 浏览 Hacktoberfest issue

Kotlin printer: review of 2026-10-15, idiom gaps and order of work for KotlinConf

未关闭
#140 4 条评论 0 个 reaction 已指派 0 人 在 GitHub 查看

还没有人认领这个 Issue。

评估

难度
5/5
预计耗时
一周以上
新手友好度
10/100
Issue 类型
重构
描述清晰度
需要澄清
活跃度
活跃
技术栈
java, kotlin
领域
tooling

调研方向

This is a review and planning ticket, not a single change: it asks for a prioritised list of printer improvements, to be split into their own issues. Start with the Kotlin printer's expression code, including KotlinNullability.receiverWithDot and the SAFE_CALL path, and with KotlinMethodPrinter:56. Done means the listed items exist as separate issues or checklist entries, each with its own acceptance criteria.

由索引模型根据 Issue 内容生成。

描述

A review of the Java → Kotlin translation (2026-10-15), against two goals: the KotlinConf claim (correct output, more idiomatic than J2K, the gain attributable to the analyses) and the longer one of a translator whose output a Kotlin developer would accept as hand-written. Sources: the ratchets, the scoreboard numbers on maddi-mod#35/#36, the printer source at f012f16e9, and the output of the #125 measurement run (build/j2k/langchain4j-preconditions-off, fernflower-preconditions-off).

Verdict. On track for the first goal; not yet for the second. The output is correct, compact, Java-shaped Kotlin: the structure is idiomatic, the expressions are not.

What is strong

  • Correctness: langchain4j-core 344/346 compiling, 1,106 tests (J2K 148 / 326); fernflower 199/199, 264 (J2K 37 / 39); guava translates in full where J2K does not complete.
  • The ablation attributes it to nullability: langchain4j 301 → 343 compiling from nullability alone.
  • Structural idioms are done: when, object :, enum class, inner, fun interface, use, synchronized, labels, IntArray, expression bodies, const val, @JvmStatic companions, smart casts after is and !!, val for once-assigned locals, records → data class.

What the output looks like

ToolExecutionResultMessage.kt (langchain4j, fresh run):

private val contents: MutableList<Content>          // built by Utils.copy, never mutated → List
this.contents = java.util.List.of(TextContent.from(builder.text))   // → listOf(...)
if (contents.size == 1 && contents.get(0) is TextContent) {return (contents.get(0) as TextContent).text() }
return (Objects.equals(this.id, that.id) && ...)    // → this.id == that.id
override fun toString(): String = "ToolExecutionResultMessage{id='" + id + '\'' + ...   // → a template
open class Builder { internal var id: String? = null ... }           // `open` on everything

MergeHelper.kt (fernflower), the densest !! region:

initExprents[1]!!.fillBytecodeRange(initExprents[1]!!.getRight()!!.bytecode)
stat.getIncExprent()!!.fillBytecodeRange(initExprents[1]!!.getRight()!!.bytecode)
val arr: ArrayExprent?
        arr = firstDoExprent.getRight() as ArrayExprent?

Every !! there is correct (short-circuit || defeats the smart cast), but a Kotlin author hoists initExprents[1]?.right into a val once. That shape is where fernflower's 2,946 !! come from: repeated calls to nullable getters, which no smart-cast pruning removes.

Translated tests: memory!!.add(...) on a fixture set in @BeforeEach (→ lateinit), UserMessage.from(("1" as String?)) (redundant cast and parentheses), java.util.List.of(m1, m2).

Idiom scorecard (printer source today)

Done Partial Absent
val/var, when from switch, object :, enums, inner, use, synchronized, labels, IntArray, lambdas/SAM, ternary → if, smart casts, lateinit, isNullOrEmpty, records → data class read-only collections (73 of 659 langchain4j declarations), properties (JDK getters only; a Java getX() stays a call), ?./?: (only under NullCheck.SAFE_CALL, off by default, or from Kotlin-parsed source), ranges (no indices/withIndex), index syntax (charAt only), also/run (generated wrappers only), variance (use-site only; multiple bounds dropped) string templates, ?./?: derived from Java null checks, let/apply, trailing lambdas and it, listOf/emptyMap, == for equals, streams → collection ops, Optional, buildString, object singletons / top-level functions, default arguments, @Throws, when from if-chains, equals/hashCode/toString → data class

Counted on the fresh langchain4j output (29.6k lines with tests): ?. 0, ?: 0, .let 0, apply 0, string templates 8 against 194 concatenations, .stream() 61, Optional 101, fun getX()/setX()/isX() 63. fernflower: !! 2,946, ?. 0, ?: 0.

Risks

  1. !! is the headline idiom number, and fernflower is 3× J2K (2,946 vs 997). J2K's output does not compile, which is the answer, but the table will be read.
  2. The modification half of the claim is thin. val/var is identical in all four ablation arms; read-only collections reach 11% of declarations. #25's world-wide parameters and locals are the remaining work; otherwise the claim should say "nullability" and present read-only types as the secondary effect.
  3. Zero ?./?: is the first thing a Kotlin audience notices. The printer has the SAFE_CALL path (KotlinNullability.receiverWithDot) and an elvis path for Kotlin-parsed source; deriving them from Java's x != null ? x.m() : null and if (v != null) v else d is local, pattern-based work.
  4. Architecture. Straight from the Java AST to text, rewrites scattered through a 2,136-line expression printer, no intermediate Kotlin model. Fast for structural idioms; each expression-level rewrite (templates, collection ops, hoisting) becomes a special case in that file.
  5. Docs and tests lag. kotlin-printing.md has no dated log and still quotes 84 compiling files; javadocs in KotlinTypeName, KotlinFieldPrinter, KotlinMethodPrinter call implemented features "refinements"; KotlinMethodPrinter:56 (static initializer as init) may be a real gap. 92 @Test methods for 7.3k lines; the ratchets measure compile-and-pass, not idiom. The idiom numbers live only in issue comments.
  6. Noise that costs nothing: open on every class and method (the closed world knows what is subclassed), one-line {x } blocks, split val arr: T? / arr = …, java.util.List.of, (x as T?).

Proposed order, for 11-25

  1. ?./?: from Java null-check shapes; == for Objects.equals / equals.
  2. Hoist repeated nullable getter calls into a val (finish KotlinValCopies): the fernflower !! lever.
  3. maddi-mod#25 across the world for parameters and locals, so the modification analysis moves a number. If it cannot land, narrow the claim's wording now.
  4. One cleanup pass: drop open where nothing in the world subclasses; listOf/emptyMap/list[i]; templates for concatenations; lateinit for @BeforeEach fixtures; brace-block formatting.
  5. Commit the scoreboard as a results file with SHAs; give kotlin-printing.md a dated log, as nullability.md has.

Parked until after the proposal: streams → collection ops, Optional, data classes from equals/hashCode/toString, getX() → properties for Java-parsed code, default arguments, object singletons.

Each item here should become its own issue or a checklist entry when picked up; this ticket is the review.

🤖 Generated with Claude Code

主要语言
Java
星标
1
派生
1
平均合并
1 小时 58 分钟
30 天内合并 PR
5

环境准备

从这里开始

  1. 先读完整个 Issue,再读项目的贡献指南。
  2. 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
  3. Fork 仓库,在一个分支上完成修改。
  4. 提交 Pull Request,并在描述里引用这个 Issue 编号。

CodeLaser/maddi 的其他 Issue

查看 CodeLaser/maddi 的全部 Issue

相似的 Issue

更多 Java Issue

把新 issue 发到你的邮箱

精选适合新手参与的 GitHub issue 摘要。