2017-01-26 16 views
1

Я пишу обертку 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.

+1

Компилятор не будет искать неявное преобразование в то, что имеет метод «map», если такой метод уже существует в исходном объекте. Это просто то, как работает алгоритм.Не знаете, как обойти это, за исключением переименования или создания собственного класса-оболочки, который создается явно, а не неявно. –

+0

Я хотел бы более полно понять, что определяет уникальность сигнатур метода. Я предполагаю, что метод «map», который принимает два аргумента вместо одного, например, не будет путать с существующим методом «map» и вызовет поиск неявного преобразования. Я также предполагаю, что 'map (x: Int)', который принимает не-функциональный тип, будет различным. Это правда? Является ли конкретная проблема здесь одним из типов стирания, что подпись компилируется до 'map (f: Function)' и, следовательно, не может отличить функциональный параметр и возвращаемые типы? –

ответ

0

Как предлагает @ Łukasz, создание явной оболочки выглядит как способ пойти сюда. И я, вероятно, обеспечит неявное преобразование из моей оболочки типа обратно KStream:

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

Таким образом, мы будем иметь как методы KStream и обертки доступны, но методы класса обертки имеют более высокий приоритет.

Очевидным недостатком является то, что пользователям при использовании исходного объекта необходимо явно использовать тип оболочки.