Java调用Scala 3内联宏代码报错:未声明类型变量B
Java 17调用Scala 3.3.0内联宏生成代码时的类型变量错误
错误信息
编译Java代码时触发如下错误:
[error] /testDir/scala3-test/src/main/scala/csw/params/Test.java:7:1: cannot access io.bullet.borer.Encoder [error] bad class file: /home/user/.cache/coursier/v1/https/repo1.maven.org/maven2/io/bullet/borer-core_3/1.11.0/borer-core_3-1.11.0.jar(/io/bullet/borer/Encoder.class) [error] undeclared type variable: B [error] Please remove or make sure it appears in the correct subdirectory of the classpath.
背景信息
- Scala 3.3.0通过borer库的
deriveCodec内联宏生成ProperMotion类的Codec实例 - Scala端代码编译正常,相同逻辑在Scala 2.13下可被Java正常调用
- Java 17代码调用Scala定义的
JsonSupport.writes方法时触发上述错误
相关代码
Scala实现
import java.nio.charset.StandardCharsets import io.bullet.borer.* import io.bullet.borer.derivation.CompactMapBasedCodecs.deriveCodec import play.api.libs.json.{Json => PJson, _} object JsonSupport extends JsonSupport trait JsonSupport { def writes[T: Encoder](x: T): JsValue = PJson.parse(Json.encode(x).toUtf8String) def reads[T: Decoder](x: JsValue): T = Json.decode(x.toString().getBytes(StandardCharsets.UTF_8)).to[T].value } case class ProperMotion(pmx: Double, pmy: Double) object ProperMotion { implicit val properMotionCodec: Codec[ProperMotion] = deriveCodec }
Java调用代码
public class Test { public void shouldConvertPmToFromJSON() { var pm = new ProperMotion(0.5, 2.33); var pmjs = JsonSupport.writes(pm, ProperMotion.properMotionCodec().encoder()); } }
borer的deriveCodec方法
inline def deriveCodec[T]: Codec[T] = Codec(deriveEncoder[T], deriveDecoder[T])
问题根源
- Scala 3内联宏生成的字节码中,
Encoder的泛型类型变量(如B)未被Java编译器正确识别——borer的Encoder实际是带多类型参数的泛型(Encoder[B, T]),Scala 3宏生成的隐式实例在字节码层面没有完整保留类型变量声明,导致Java无法解析。 - Java代码直接传递
encoder()的调用方式不符合Scala隐式参数的设计逻辑,进一步放大了类型解析的兼容性问题。
解决方案
方案1:提供Java友好的重载方法(推荐)
修改Scala的JsonSupport trait,添加直接接收Codec实例的重载方法,绕过隐式参数的兼容性问题:
trait JsonSupport { def writes[T: Encoder](x: T): JsValue = PJson.parse(Json.encode(x).toUtf8String) // 新增Java友好的重载,直接接收Codec def writes[T](x: T, codec: Codec[T]): JsValue = writes(x)(codec.encoder, codec.decoder) def reads[T: Decoder](x: JsValue): T = Json.decode(x.toString().getBytes(StandardCharsets.UTF_8)).to[T].value // 同样为reads添加重载 def reads[T](x: JsValue, codec: Codec[T]): T = reads(x)(codec.decoder) }
Java代码调整为调用新的重载方法:
public class Test { public void shouldConvertPmToFromJSON() { var pm = new ProperMotion(0.5, 2.33); var pmjs = JsonSupport.writes(pm, ProperMotion.properMotionCodec()); } }
方案2:显式指定完整泛型类型
如果无法修改Scala代码,可在Java中显式声明Encoder的完整泛型参数,帮助编译器解析类型变量:
import io.bullet.borer.Encoder; import io.bullet.borer.json.Json; public class Test { public void shouldConvertPmToFromJSON() { var pm = new ProperMotion(0.5, 2.33); Encoder<Json.Output, ProperMotion> encoder = ProperMotion.properMotionCodec().encoder(); var pmjs = JsonSupport.writes(pm, encoder); } }
方案3:升级borer版本
检查borer的最新稳定版,部分版本针对Scala 3与Java的交互做了兼容性修复,升级库版本可能直接解决宏生成字节码的问题。
内容的提问来源于stack exchange,提问作者Allan
相关产品推荐
相关产品推荐

