我可以让Scala更喜欢隐式转换为Java 8 Lambda吗?

时间:2017-01-26 02:48:33

标签: scala java-8 implicit-conversion

我正在为kafka-streams库编写一个Scala包装器。基本方法是提供从kafka-streams类(如KStream)到富KStreamOps类的隐式转换。

例如,KStream提供以下map签名:

<K1, V1> KStream<K1, V1> map(KeyValueMapper<K, V, KeyValue<K1, V1>> mapper)

其中KeyValueMapper是带签名的SAM(来自Scala视角)(K, V) => KeyValue[K1, V1]

这是KStreamOps提供map方法的最小实现:

class KStreamOps[K, V](underlying: KStream[K, V]) {
  def map[K1, V1](f: (K, V) => (K1, V1)): KStream[K1, V1] =
    underlying.map { (k, v) =>
      val tuple = f(k, v)
      KeyValue.pair(tuple._1, tuple._2)
    }
}

请注意,map此处需要一个返回Tuple2的函数并处理将其转换为kafka-streams特定KeyValue类型的函数,以便用户不会需要直接与KeyValue互动。

我提供隐式转换:

implicit def ops[K, V](kstream: KStream[K, V]): KStreamOps[K, V] = new KStreamOps[K, V](kstream)

但是当我尝试在以下代码中使用我的新map时:

val inputStream: KStream[Int, String] = builder.stream(inputTopic)
stream.map{ (k, v) => (k, v) }

我收到以下编译错误:

[error] KStreamOpsTest.scala:47: type mismatch;
[error]  found   : (Int, String)
[error]  required: org.apache.kafka.streams.KeyValue[Int,String]
[error]       stream.map((k, v) => (k, v))
[error]                            ^
[error] KStreamOpsTest.scala:47: Could not derive subclass of org.apache.kafka.streams.kstream.KeyValueMapper[Int,String,org.apache.kafka.streams.KeyValue[Int,String]]
[error]  (with SAM `def method apply(x$1: Int, x$2: String)org.apache.kafka.streams.KeyValue[Int,String]`)
[error]  based on: ((k: Int, v: String) => scala.Tuple2(k, v)).
[error]       stream.map((k, v) => (k, v))

因此,编译器正在尝试使用期望KStream#map的SAM而不是KeyValue来使用KStreamOps#map,而Tuple2期望函数的输出为{{} 1}}。

如果我将方法重命名为KStreamOps#map1并调用stream.map1,隐式转换将按预期工作,并且代码会编译。

为什么编译器无法识别出隐式转换会获得一个采用正确签名函数的方法?不考虑功能返回类型吗? SAM会不会赢?

我正在使用Scala 2.11.8对其进行测试,并打开-Xexperimental标记。

1 个答案:

答案 0 :(得分:0)

正如@Łukasz所说,创建一个明确的包装器似乎就是这里的方式。我可能会提供从包装类型到KStream的隐式转换:

implicit def unwrap[K, V](wrapped: WrappedKStream[K, V]): KStream[K, V] = wrapped.underlying

这样我们就可以使用KStream和包装器的方法,但包装类方法优先。

这里一个明显的缺点是用户在创建初始对象时需要显式使用包装类型。