我正在为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
标记。
答案 0 :(得分:0)
正如@Łukasz所说,创建一个明确的包装器似乎就是这里的方式。我可能会提供从包装类型到KStream
的隐式转换:
implicit def unwrap[K, V](wrapped: WrappedKStream[K, V]): KStream[K, V] = wrapped.underlying
这样我们就可以使用KStream
和包装器的方法,但包装类方法优先。
这里一个明显的缺点是用户在创建初始对象时需要显式使用包装类型。