Я пишу обертку Scala для библиотеки kafka-streams. Основной подход заключается в предоставлении неявных преобразований из классов kafka-streams, таких как KStream
, в богатый класс KStreamOps
.Могу ли я заставить Scala предпочесть неявное преобразование в Java 8 Lambda?
Например, KStream
предоставляет следующую подпись: map
<K1, V1> KStream<K1, V1> map(KeyValueMapper<K, V, KeyValue<K1, V1>> mapper)
, где KeyValueMapper
является САМ с подписью (с точки зрения 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
типа, так что пользователь Безразлично» t необходимо напрямую взаимодействовать с 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
.
Если я переименую свой метод на KStreamOps#map1
и позвонил stream.map1
, неявное преобразование работает как ожидалось, и код компилируется.
Почему компилятор не может распознать, что существует неявное преобразование, которое получит метод, который выполняет функцию правильной подписи? Возвращается ли тип возвращаемой функции? Будет ли SAM всегда побеждать?
Я тестирую это с помощью Scala 2.11.8 с включенным флагом -Xexperimental
.
Компилятор не будет искать неявное преобразование в то, что имеет метод «map», если такой метод уже существует в исходном объекте. Это просто то, как работает алгоритм.Не знаете, как обойти это, за исключением переименования или создания собственного класса-оболочки, который создается явно, а не неявно. –
Я хотел бы более полно понять, что определяет уникальность сигнатур метода. Я предполагаю, что метод «map», который принимает два аргумента вместо одного, например, не будет путать с существующим методом «map» и вызовет поиск неявного преобразования. Я также предполагаю, что 'map (x: Int)', который принимает не-функциональный тип, будет различным. Это правда? Является ли конкретная проблема здесь одним из типов стирания, что подпись компилируется до 'map (f: Function)' и, следовательно, не может отличить функциональный параметр и возвращаемые типы? –