2013-09-23 4 views
5

Я пытаюсь вызвать этот метод set документально here, в библиотеке Java jOOQ, с подписью:Почему Scala не использует здесь неявное преобразование?

<T> ... set(Field<T> field, T value) 

Эта линия Scala проблема:

.set(table.MODIFIED_BY, userId) 

MODIFIED_BY является Field<Integer> представляющий столбец таблицы. userId является Int. Predef имеет неявное преобразование от Int до Integer, так почему же он не использует его? Я получаю это:

type mismatch; found: org.jooq.TableField[gen.tables.records.DocRecord,Integer]  
      required: org.jooq.Field[Any] 
Note: Integer <: Any 
(and org.jooq.TableField[gen.tables.records.DocRecord,Integer] <: 
    org.jooq.Field[Integer]), but Java-defined trait Field is invariant in type T. 
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10) 

Update - О примере Виниций в

Вместо того, чтобы попытаться объяснить это в комментариях, вот демонстрация того, что не существует неявное преобразование вызывается, когда вы используете тип с коварианта параметр, такой как List[+T]. Скажем, я ставлю этот код в файл, скомпилировать и запустить его ...

case class Foo(str: String) 

object StackOver1 extends App { 

    implicit def str2Foo(s: String): Foo = { 
    println("In str2Foo.") 
    new Foo(s) 
    } 

    def test[T](xs: List[T], x: T): List[T] = { 
    println("test " + x.getClass) 
    xs 
    } 

    val foo1 = new Foo("foo1") 
    test(List(foo1), "abc") 
} 

Вы увидите, что он вызывает тест, но никогда не неявное преобразование из String «ABC» в Foo. Вместо этого он выбирает T для test[T], который является общим базовым классом между String и Foo. Когда вы используете Int и Integer, он выбирает Any, но это сбивает с толку, потому что представление времени выполнения Int в списке - Integer. Таким образом, похоже, что он использовал неявное преобразование, но это не так. Вы можете проверить, открыв приглашение Scala ...

scala> :type StackOver1.test(List(new java.lang.Integer(1)), 2) 
List[Any] 

ответ

2

Я не знаю ничего aboutjOOQ, но я думаю, что проблема в том, что Scala не понимает Java дженериков очень хорошо. Попытка:

scala> def test[T](a : java.util.ArrayList[T], b: T) = { println(a,b) } 
scala> val a = new java.util.ArrayList[Integer]() 
scala> val b = 12 
scala> test(a,b) 
<console>:11: error: type mismatch; 
found : java.util.ArrayList[Integer] 
required: java.util.ArrayList[Any] 
Note: Integer <: Any, but Java-defined class ArrayList is invariant in type E. 
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10) 
       test(a,b) 

Звучит знакомо ???

И исправить, просто сообщите типу T, чтобы вызвать метод: test[Integer](a,b) отлично работает.

EDIT:

Там несколько вещей, участвующих здесь:

  1. Erasure -> При компиляции типа родовое отпадет стиранием. Компилятор будет использовать Object, который Scala будет обрабатывать как Any. Однако ArrayList [Integer] не является ArrayList [Any], хотя Integer является любым. Точно так же, как TableField [gen.tables.records.DocRecord, Integer] не является полем [Any].

  2. Механизм ввода типа -> он будет определять, какой тип T должен быть, и для этого он будет использовать доминант пересечения пройденных типов (в нашем случае первый общий предок). Страница 36 из Scala Language Spec, которая в наших примерах выше приведет к использованию Any.

  3. Неявное преобразование -> это последний шаг и будет вызываться, если бы какой-то тип был преобразован в другой, но поскольку тип аргументов был определен как первый общий предок, нет необходимости конвертировать и мы никогда не будем иметь неявное преобразование, если мы не заставляем типа T.

примера, чтобы показать, как общий предок используется для определения Т:

scala> def test[T](a: T, b: T): T = a 
scala> class Foo 
scala> class Boo extends Foo 
scala> test(new Boo,new Foo) 
res2: Foo = [email protected] 
scala> test(new Boo,new Boo) 
res3: Boo = [email protected] 
scala> class Coo extends Foo 
scala> test(new Boo,new Coo) 
res4: Foo = [email protected] 
scala> test(new Boo,"qsasad") 
res5: Object = [email protected] 

Подытоживая , неявный метод не вызывается, поскольку тип метода вывода, определяет типы перед получением аргумента, и поскольку он использует общего предка, нет необходимости в неявном преобразовании.

Ваш код вызывает ошибку из-за механизма стирания, который исчезает с информацией о типе, которая была бы важна для определения правильного типа аргумента.

@RobN, спасибо за допрос моего ответа, я многому научился с процессом.

+0

Хорошо, спасибо. Я понимаю разницу дисперсий между List и ArrayList, но, к сожалению, добавление [Integer] в этом случае не очень хорошо. Это произойдет слишком много и сделает код уродливым. Я хотел бы знать, почему неявный не используется. –

+0

Я приму этот ответ, если найду что-нибудь в спецификации документов или языка, в которой говорится, почему он не считает неявным. Возможно, все параметры типа решаются до рассмотрения имплицитов. Если бы это было не так, он мог бы использовать 'ArrayList [Integer]' вместо 'ArrayList [Any]'. Вы, например, не совсем правы ... это не означает «принудительное неявное преобразование». Это запутанно в этом случае, потому что scala использует 'Integer' во время выполнения, потому что это' Int', но ваша тестовая функция просто получает «List [Any]». Вызывается неявная функция преобразования. –

+0

@RobN, пожалуйста, помогите мне улучшить ваш ответ. Посмотрим, смогу ли я это сделать. Первый пример воспроизводит вашу ошибку. Компилятор не знает, как сопоставить (javageneric, Int) в (javageneric , T). Второй в Scala показывает неявное преобразование, заметим, что b преобразуется в java.lang.Integer. Я не знаю, где это находится в документации, я просто показал вам, экспериментируя. Если у вас есть пример, который может доказать, что я ошибаюсь, сделайте это. –

Смежные вопросы