Scala调用Java方法的实现方式及TokenRequest构造函数重载解析错误解决办法
1. 如何在Scala程序中调用Java中定义的方法?
Scala和Java天生互操作性拉满,调用Java方法其实和写Scala代码没太大差别,给你梳理几个关键场景的操作方式:
- 直接调用实例方法:先导入对应的Java类,创建实例后直接调用方法就行,和Java写法几乎一致。比如你有个Java工具类
JavaUtils,里面有个doSomething(String)方法:import com.example.JavaUtils val utils = new JavaUtils() utils.doSomething("hello from Scala") - 调用静态方法:Java的静态方法在Scala里直接通过类名调用就行,不用实例化:
import com.example.JavaMathUtils val sumResult = JavaMathUtils.calculateSum(1, 2, 3) - 处理集合互转:Scala和Java的集合是两套体系,如果要把Scala集合传给Java方法,或者反过来,记得用
scala.jdk.CollectionConverters(Scala 2.13及以上版本推荐)来转换:import scala.jdk.CollectionConverters._ // Scala List转Java List val scalaList = List("a", "b", "c") val javaList = scalaList.asJava // Java List转Scala List val javaArrayList = new java.util.ArrayList[String]() javaArrayList.add("x") val scalaListFromJava = javaArrayList.asScala.toList - 处理Java的null:Scala里更推荐用
Option来避免空指针,但调用Java方法时如果遇到返回null的情况,可以直接处理,或者用Option()把它包装成Scala的可选类型,更安全:val javaResult = someJavaMethodThatMightReturnNull() val safeScalaResult = Option(javaResult) // 变成Option[T],避免NPE
2. 解决TokenRequest构造函数重载无法解析的问题
你的代码里主要有三个小问题导致了这个报错,咱们一步步来修正:
问题1:val变量不能重新赋值
Scala里val是不可变变量,一旦声明初始化后就不能再改值。你一开始把tokenRequest定义成val tokenRequest: TokenRequest = null,后面又尝试tokenRequest = new TokenRequest(...),这在Scala里是不允许的。咱们可以改成用var,不过更优雅的方式是直接用if表达式返回结果(因为Scala的if是有返回值的,不用额外定义变量)。
问题2:Scala List和Java List类型不兼容
你用List(audience)创建的是Scala的List,但ConcurrentHashMap需要的是Java的java.util.List,直接传会导致类型不匹配,进而让构造函数找不到合适的重载版本。这时候需要用CollectionConverters把Scala List转成Java List。
问题3:Scope参数传错了
当scopes不为null时,你传的是Scope.parse(""),这会解析出一个空的Scope,应该改成解析传入的scopes参数,也就是Scope.parse(scopes)。
对照官方给出的构造函数参数:TokenRequest(URI, ClientAuthentication, AuthorizationGrant, Scope, List<String>, Map<String, List<String>>),第五个参数是List<String>类型的claims(可以传null),你之前传null是没问题的,主要是前面的List类型要注意是Java的。
下面是修正后的完整代码:
import scala.jdk.CollectionConverters._ import java.net.URI import java.util.concurrent.ConcurrentHashMap import com.nimbusds.oauth2.sdk.{TokenRequest, ClientCredentialsGrant, Scope} import com.nimbusds.oauth2.sdk.auth.ClientAuthentication private def makeTokenRequest(format: String, provider: String, scopes: String, audience: String, clientAuthentication: ClientAuthentication): TokenRequest = { val uri = new URI(baseUrl + "?provider=" + provider + "&format=" + format) val params = new ConcurrentHashMap[String, java.util.List[String]]() if (audience != null) { // 把Scala List转成Java List,适配Java集合类型 params.put("audience", List(audience).asJava) } // 用if表达式直接返回结果,不用额外定义变量,更符合Scala风格 if (scopes != null) { new TokenRequest( uri, clientAuthentication, new ClientCredentialsGrant(), Scope.parse(scopes), null, params ) } else { new TokenRequest( uri, clientAuthentication, new ClientCredentialsGrant(), null, null, params ) } }
给你解释下关键改动:
- 导入了
scala.jdk.CollectionConverters._,用来做Scala和Java集合的转换; - 明确了
params的类型为ConcurrentHashMap[String, java.util.List[String]],避免类型推断出错; - 用
List(audience).asJava把Scala List转成Java List,适配Java方法的参数要求; - 去掉了不必要的
tokenRequest变量,直接在if分支里返回新的TokenRequest实例,更简洁; - 修正了Scope的解析参数,从空字符串改成传入的
scopes,符合业务逻辑。
这样应该就能顺利解决构造函数无法解析的问题了。
内容的提问来源于stack exchange,提问作者user15826601

