生而为人

程序员的自我修养

0%

[toc]

重载歧义问题

在 Scala 中调用 Java 重载方法时,即使只传递一个参数,也可能遇到重载歧义错误,这通常与 存在类型(existential type) 和 Scala 编译器的类型推断策略有关。下面解释原因及解决方法。


1. 问题根源

Reflections 库的两个重载方法签名如下:

java

复制下载

1
2
public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation)
public Set<Class<?>> getTypesAnnotatedWith(Class<? extends Annotation> annotation, boolean honorInherited)

在 Scala 中调用 getTypesAnnotatedWith(classOf[Problem]) 时:

  • 参数 classOf[Problem] 的类型是 Class[Problem]
  • 两个方法的第一个参数类型均为 Class[_ <: Annotation](Scala 对 ? extends 的表示)。
  • Scala 允许将 Class[Problem] 赋值给 Class[_ <: Annotation],因为 ProblemAnnotation 的子类型,且存在类型是协变的。因此,两个方法都接受第一个参数的类型

按理说,第二个方法还需要一个 boolean 参数,而调用时只提供了一个参数,所以它应该被排除。但问题出在 Scala 的重载解析规则上:

  • 编译器会计算每个方法的适用性,考虑隐式转换、默认参数等。对于 Java 方法,默认参数不存在,隐式转换也可能被考虑。
  • 实际上,第二个方法也因为第一个参数类型匹配而被视为“可能适用”,但参数个数不匹配。然而,Scala 编译器在某些情况下会因存在类型而无法准确判断参数个数,或者认为第二个方法有可能通过隐式参数补全(即使不存在),导致歧义。

根本原因:Java 通配符 (? extends) 被映射为 Scala 的存在类型,这使得类型系统变得复杂。当两个重载方法的第一个参数类型在存在类型下被视为“相同”时,编译器需要额外的类型信息来区分它们。而只提供一个参数时,第二个方法在存在类型层面仍然可能被考虑,从而引发“无法解析重载方法”的错误。


2. 示例验证

尝试以下代码(假设 Problem 是注解):

scala

复制下载

1
2
3
4
5
import org.reflections.Reflections

val reflections = new Reflections("com.example")
// 这行可能报错:Cannot resolve overloaded method
reflections.getTypesAnnotatedWith(classOf[Problem])

错误信息提示有多个重载方法适用,但实际上只有第一个方法参数个数匹配。这是因为编译器在存在类型转换后,无法唯一确定应该使用哪个方法。


3. 解决方案

方法一:类型归属(推荐)

显式指定参数类型,帮助编译器消除歧义:

scala

复制下载

1
reflections.getTypesAnnotatedWith(classOf[Problem]: Class[Problem])
方法二:使用双参数版本

提供第二个参数(通常为 true),参数个数不同即可区分:

scala

复制下载

1
reflections.getTypesAnnotatedWith(classOf[Problem], true)
方法三:将参数赋给显式类型变量

scala

复制下载

1
2
val annotationClass: Class[Problem] = classOf[Problem]
reflections.getTypesAnnotatedWith(annotationClass)

4. 为什么类型归属有效?

类型归属 : Class[Problem] 将表达式的类型明确固定为 Class[Problem],而不是允许编译器推断为存在类型 Class[_ <: Annotation]。这样,第一个方法的参数类型变为 Class[Problem],而第二个方法需要的仍是 Class[_ <: Annotation],此时两个方法的第一个参数类型不再相同,加上参数个数的差异,编译器就能唯一选择第一个方法。


5. 总结

  • Scala 调用 Java 重载方法时,如果参数涉及通配符(存在类型),可能会因类型系统交互而产生重载歧义。
  • 即使参数个数不同,也可能因存在类型的存在而导致编译器无法区分。
  • 通过显式类型归属或提供完整参数列表,可以消除歧义。

注意点:

  1. 系统编码
  2. 程序中指定编码

case

  1. 微软spark机器写出中文都是问号乱码。输出了系统文件编码System.getProperty("file.encoding"), 是cp1252. 如何处理能正常输出中文?

编码简介

编码 介绍
Windows-1252 或CP-1252 是拉丁字母的字元編碼,主要用於英文、 愛爾蘭語、義大利語、挪威語、葡萄牙語、西班牙語、瑞典語以及德語、芬蘭語、冰島語、法語、荷蘭語(除了IJ字符)、斯洛維尼亞語(除了č字符)等西方文字版本Microsoft Windows 的預設編碼,為Windows代碼頁之一。

编码转换

编码判断

参考:

  1. java cp1252,将cp-1252编码为utf-8?

待整理

  1. 各种环境/语言中设置字符编码

SOP(Standard Operating Procedure) 标准作业程序

Feed流

https://km.sankuai.com/page/67740541

开箱即用

开箱即用功能(out-of-the-box feature,也称OOTB或off the shelf)在软件上指产品在安装后,无需配置或修改,即可使用的功能或特性。也指默认即对所有用户可用,不需支付额外费用或进行另外的配置。
我们常说的spring-boot或者vue组件等可以开箱即用,就是可以无需配置就可以实现大部分功能,但是他们依旧是可以配置的。(强调约定优于配置)

time to live (TTL)

在函数层面 尽量保证什么类型进什么类型出,除非本函数有转换类型的作用