【发布时间】:2015-11-03 11:42:37
【问题描述】:
考虑以下火花代码:
package test
import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.Function;
import java.io.Serializable;
import java.util.Arrays;
abstract class Ops implements Serializable {
public void doSomething(JavaRDD<Integer> rdd, Function<Integer, Integer> func) {
rdd.map(x -> x + func.call(x))
.collect()
.forEach(System.out::println);
}
}
public class AbstractTest {
public static void main(String[] args) {
new AbstractTest().job();
}
public void job() {
SparkConf conf = new SparkConf()
.setAppName(AbstractTest.class.getName())
.setMaster("local[*]");
JavaSparkContext jsc = new JavaSparkContext(conf);
JavaRDD<Integer> rdd = jsc.parallelize(Arrays.asList(1, 2, 3, 4, 5, 6));
Ops ops = new Ops() {};
Function<Integer, Integer> f1 = v1 -> v1 + 1; // works
Function<Integer, Integer> f2 = new Function<Integer, Integer>() {
@Override
public Integer call(Integer v1) throws Exception {
return v1 + 1;
}
}; // java.io.NotSerializableException of non-serializable outer class
ops.doSomething(rdd, f1);
}
}
在我的例子中,我想弄清楚函数式接口和 lambda 表达式之间的区别:
基本上,抽象类Ops中的函数doSomething需要序列化一个已经是可序列化闭包的spark.api.java.function.Function。
在初始化Function对象时,我使用了两种不同的方式:
f1:lambda 表达式
f2:显式函数接口
f1 有效,f2 无效。我发现他们有不同的类名:
f1:类 test.AbstractTest$$Lambda$8/783882192
f2:类 test.AbstractTest$2
f2 似乎被编译为匿名类,在 java 中强烈不鼓励对其进行序列化,并且其外部类也需要序列化。在我的例子中,外部类 AbstractTest 是不可序列化的。这就是我们收到NotSerializableException 的原因。
另一方面,f1 编译为$Lambda$8/783882192,我不明白它代表什么。但显然,它是与匿名类不同的实现。它工作正常。好像可以避免序列化外部类。
那么,函数式接口和 lambda 表达式有什么区别呢?在这种特殊情况下,它们似乎并不等价。
【问题讨论】:
标签: java serialization lambda apache-spark