Spark向Scala类构造函数添加隐藏参数,致自定义InputFormat无法传入hadoopFile
这个现象其实是Spark Shell的交互式环境特性导致的,和Scala语言本身无关。
为什么会出现隐藏参数?
Spark Shell(包括增强版的Scala REPL)在交互式定义类时,会自动给类的构造函数注入一个隐式参数,用来关联Shell的上下文环境(比如当前的SparkSession、变量作用域等),方便在交互式场景中快速测试代码。但这个行为只在Spark Shell里存在,常规Scala编译环境(比如sbt/maven编译的项目)中定义的类不会有这个额外参数。
你在Shell里执行的代码也验证了这一点:
scala> class A {} defined class A scala> classOf[A].getConstructors()(0).getAnnotatedParameterTypes res0: Array[java.lang.reflect.AnnotatedType] = Array(sun.reflect.annotation.AnnotatedTypeFactory$AnnotatedTypeBaseImpl@5ed65e4b)
为什么影响自定义InputFormat?
Hadoop的InputFormat(以及很多Hadoop生态的类)有一个硬性要求:必须提供无参构造函数。因为Hadoop在分布式任务中需要通过反射实例化这些类,而反射调用的默认逻辑就是寻找无参构造方法。Spark Shell中注入的隐藏参数让你的类构造函数不再是无参的,所以当你把自定义InputFormat传给hadoopFile时,Hadoop反射实例化就会失败。
解决方法
1. 推荐:外部编译自定义类(最可靠)
不要在Spark Shell里定义需要无参构造的类,而是把自定义的InputFormat类写到单独的Scala源文件中,用sbt或maven编译成jar包,然后通过以下方式引入Spark Shell:
spark-shell --jars your-custom-inputformat.jar
这样编译出来的类会有正常的无参构造函数,完全符合Hadoop的要求。
2. 临时测试:显式声明无参构造(仅作临时验证)
如果只是想在Shell里临时测试,你可以尝试显式声明无参构造函数,但这个方法不一定100%可靠(因为Spark Shell的注入逻辑可能还是会修改构造函数):
class CustomInputFormat extends InputFormat[K, V] { // 显式声明无参构造 def this() = this() // 实现InputFormat的抽象方法... }
但还是强烈推荐第一种方法,因为这是分布式场景下的标准做法。
内容的提问来源于stack exchange,提问作者abufct

