2013-09-23 13 views
5

Próbuję zadzwonić ta metoda set udokumentowane here w bibliotece Java jOOQ, z podpisem:Dlaczego Scala nie używa tutaj konwersji niejawnej?

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

Linia ta Scala jest problem:

.set(table.MODIFIED_BY, userId) 

MODIFIED_BY jest Field<Integer> reprezentujący kolumna tabeli. userId to Int. Predef ma niejawną konwersję z Int na Integer, więc dlaczego jej nie używa? Otrzymuję ten:

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) 

Aktualizacja - O przykładu Vinicius

Zamiast próbować tłumaczyć to w komentarzach, tutaj jest wykazanie, że nie istnieje niejawna konwersja miano podczas korzystania typ z kowariantna parametr, taki jak List[+T]. Powiedzmy, że mogę umieścić ten kod w pliku, skompilować i uruchomić go ...

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") 
} 

Zobaczysz, że nazywa test, ale nigdy nie jest niejawna konwersja z String „abc” do Foo. Zamiast tego wybiera T dla test[T], która jest wspólną podstawową klasą między String i Foo. Podczas korzystania z Int i Integer wybiera ona Any, ale jest to mylące, ponieważ reprezentacja środowiska wykonawczego Int na liście to Integer. Wygląda więc na to, że użył niejawnej konwersji, ale tak się nie stało. Można sprawdzić, otwierając wiersz Scala ...

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

Odpowiedz

2

nie wiem nic aboutjOOQ, ale myślę, że problemem jest to, że nie rozumie Scala rodzajowych java bardzo dobrze. Wypróbuj:

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) 

Brzmi znajomo?

I do naprawy, wystarczy poinformować typ T, aby wywołać metodę: test[Integer](a,b) działa dobrze.

EDIT:

Nie

kilka rzeczy zaangażowane tutaj:

  1. Erasure -> Kiedy skompilowany typ rodzajowy zniknie skasowaniem. Kompilator użyje Obiektu, którego Scala, potraktuje jako Dowolny. Jednak ArrayList [Integer] nie jest ArrayList [Any], nawet jeśli liczba całkowita jest dowolna. W taki sam sposób, jak TableField [gen.tables.records.DocRecord, Integer] nie jest polem [Any].

  2. Mechanizm inferencji typu -> to, jaki typ T powinien być i do czego będzie używał przecięcia dominator typów przekazywanych (w naszym przypadku pierwszy wspólny przodek). Strona 36 z Scala Language Spec, która w naszych przykładach powyżej doprowadzi do użycia Any.

  3. Konwersja niejawna -> jest to ostatni krok i zostanie wywołana, jeśli istnieje jakiś typ do konwersji na inny, ale ponieważ typ argumentów został określony jako pierwszy wspólny przodek, nie ma potrzeby konwertować i nigdy nie będzie miał niejawna konwersja jeżeli nie zmusić typu T.

przykładem, aby pokazać jak wspólny przodek jest używany do określenia 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] 

Reasumując , metoda niejawna nie jest wywoływana, ponieważ typ mechanizmu wnioskowania, określa typy przed uzyskaniem argumentu i ponieważ używa wspólnego przodka, nie ma potrzeby niejawnej konwersji.

Twój kod generuje błąd związany z mechanizmem usuwania, który znika wraz z informacją o typie, która byłaby ważna dla określenia prawidłowego typu argumentu.

@RobN, dzięki za przesłuchanie mojej odpowiedzi, wiele się nauczyłem z tym procesem.

+0

Okay, dzięki. Rozumiem różnicę wariancji między List i ArrayList, ale niestety dodanie [Integer] nie jest dobre w tym przypadku. To by się stało zbyt wiele i sprawiłoby, że kod byłby brzydki. Chciałbym wiedzieć, dlaczego nie jest używany niejawny. –

+0

Przyjmuję tę odpowiedź, jeśli znajdę coś w dokumentacji lub specyfikacji językowej, która mówi, dlaczego nie bierze pod uwagę tego, co ukryte. Być może wszystkie parametry typu są ustalane przed uwzględnieniem implikacji. Gdyby tak nie było, mógłby użyć 'ArrayList [Integer]' zamiast 'ArrayList [Any]'. Twój przykład nie jest całkiem poprawny ... nie "wymusza niejawnej konwersji". W tym przypadku jest to mylące, ponieważ scala używa 'Integer' w czasie wykonywania dla' Int', ale twoja funkcja testowa otrzymuje po prostu 'List [Any]'. Nie jest wywoływana żadna niejawna funkcja konwersji. –

+0

@RobN, pomóż mi poprawić odpowiedź. Zobaczmy, czy mogę to wyjaśnić. Pierwszy przykład powtarza Twój błąd. Kompilator nie wie, jak dopasować (javageneric, Int) do (javageneric , T). Drugi w Scali pokazuje niejawną konwersję, zauważ, że b jest konwertowane na java.lang.Integer. Nie wiem, gdzie to jest w dokumentacji, po prostu pokazałem ci, eksperymentując. Jeśli masz przykład, który może mi pomóc, proszę. –

Powiązane problemy