Opcja Scala (null) oczekiwana jako Brak, ale dostałem Trochę (0)

9
val i: java.lang.Integer = null
val o: Option[Int] = Option(i) // This yields Some(0)

Jaki jest bezpieczny sposób konwersji null: java.lang.Integerna Scalę Option[Int]?

anandhu sreekumar
źródło
1
Czy możesz wkleić kod w pytaniu?
Виталий Олегович

Odpowiedzi:

17

Mieszacie Inti java.lang.Integertak dalej

val i: java.lang.Integer = null
val o: Option[Int] = Option(i)

pośrednio konwertuje na

val o: Option[Int] = Option(Integer2int(i))

który staje się

val o: Option[Int] = Option(null.asInstanceOf[Int])

a zatem

val o: Option[Int] = Some(0)

Jeśli chcesz pracować java.lang.Integer, napisz

val o: Option[java.lang.Integer] = Option(i)
// o: Option[Integer] = None
Mario Galic
źródło
2
Niedawno gdzieś o to pytano, więc to prawdziwa gotcha. Być może problem polega na wnioskowaniu: Option[Integer](i).map(_.intValue)wydaje mi się najbardziej idiomatyczny, ponieważ mówi, co robi. Użyj także, -Xlintaby zobaczyć ostrzeżenie dla val o!
som-snytt
Aby uniknąć obstawiania boksu, Integerwywnioskowano `val x: Opcja [Int] = Opcja (i) .asInstanceOf [Opcja [Int]]`
som-snytt
7

To wydaje się dzieje dlatego, że tworzenie Optioni przekształcenie go do Intw jednym kroku (@ MarioGalic za odpowiedź wyjaśnia, dlaczego tak się dzieje).

Robi to, co chcesz:

scala> val o: java.lang.Integer = null
o: Integer = null

scala> val i: Option[Int] = Option(o).map(_.toInt)
i: Option[Int] = None

scala> val o1: java.lang.Integer = 1
o1: Integer = 1

scala> val i1: Option[Int] = Option(o1).map(_.toInt)
i1: Option[Int] = Some(1)
Jack Leow
źródło
Z drugiej strony zasugerowałem _.intValue. Wydaje mi się, że zapisuje tylko konwersję.
som-snytt
1

Wcześniej ten sam problem. To wątpliwe zachowanie jest znane zespołowi Scali. Wygląda na to, że zmiana go psuje coś innego. Zobacz https://github.com/scala/bug/issues/11236 i https://github.com/scala/scala/pull/5176 .

simpadjo
źródło
2
Naprawdę wątpliwe zachowanie traktuje się nulljako liczbę całkowitą. Jest to prawdopodobnie kac, z Cktórego można przypisać 0wskaźnik. Ale to nie znaczy, że wynikowy wskaźnik jest 0taki, więc przełączanie się między nimi jest niejasne C.
Tim
Integernajprawdopodobniej pochodzi z kodu Java, więc „nie traktuj wartości null jako liczby całkowitej” nie jest praktyczną radą. I jawnie sprawdzamy tę liczbę całkowitą pod kątem używania zerowalności Option.apply. Otrzymujemy więc nieoczekiwany wynik bez jawnego wykonywania niebezpiecznych operacji.
simpadjo
Chodzi o to, że nie można winić Scali za „wątpliwe zachowanie”, gdy główną przyczyną jest Java. Poradą praktyczną jest jawna konwersja z typów Java na równoważne typy Scala zamiast korzystania z niejawnej konwersji. (Stąd JavaConvertersraczej niż JavaConversion)
Tim
1
Cóż, mogę i obwiniam Scalę za to, że w tym przypadku nie wyemitował błędu kompilacji / ostrzeżenia. Nawet awaria środowiska uruchomieniowego byłaby lepsza. Nawet w mojej samej firmie 2 programistów z ponad 5-letnim doświadczeniem w Scali dotknęło tego problemu.
simpadjo
1
@Tim Bardzo łatwo byłoby uzyskać awarię środowiska wykonawczego, po prostu dzwoniąc theInteger.intValue(). Uniknięcie tej awarii kosztuje dodatkowe sprawdzenie środowiska wykonawczego. W starszych wersjach Scali ta konwersja rzeczywiście spowodowała powstanie NPE; został zgłoszony jako błąd i naprawiony do bieżącego zachowania. Nie jestem ekspertem Scala, ale wykopałem scala-dev # 355 i scala # 5176 jako kontekst historyczny.
amalloy