Smutnym faktem z życia Scali jest to, że jeśli tworzysz List [Int], możesz sprawdzić, czy twoja instancja jest Listą, i możesz zweryfikować, czy każdy jej pojedynczy element to Int, ale nie to, że jest Listą [ Int], jak można łatwo zweryfikować:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
warning: there were unchecked warnings; re-run with -unchecked for details
A list of strings?!
Opcja -unchecked obciąża winę za kasowanie typu:
scala> List(1,2,3) match {
| case l : List[String] => println("A list of strings?!")
| case _ => println("Ok")
| }
<console>:6: warning: non variable type-argument String in type pattern is unchecked since it is eliminated by erasure
case l : List[String] => println("A list of strings?!")
^
A list of strings?!
Dlaczego tak jest i jak sobie z tym poradzić?
scala
type-erasure
Daniel C. Sobral
źródło
źródło
TypeTag
s .scala 2.10.2
zobaczyłem to ostrzeżenie:<console>:9: warning: fruitless type test: a value of type List[Int] cannot also be a List[String] (but still might match its erasure) case list: List[String] => println("a list of strings?") ^
uważam, że twoje pytanie i odpowiedź są bardzo pomocne, ale nie jestem pewien, czy to zaktualizowane ostrzeżenie jest przydatne dla czytelników.Odpowiedzi:
Scala została zdefiniowana za pomocą Type Erasure, ponieważ Java Virtual Machine (JVM), w przeciwieństwie do Javy, nie otrzymywała generycznych. Oznacza to, że w czasie wykonywania istnieje tylko klasa, a nie parametry jej typu. W tym przykładzie JVM wie, że obsługuje a
scala.collection.immutable.List
, ale nie że ta lista jest sparametryzowanaInt
.Na szczęście w Scali jest funkcja umożliwiająca obejście tego. To manifest . Manifest to klasa, której instancje są obiektami reprezentującymi typy. Ponieważ te instancje są obiektami, możesz je przekazywać, przechowywać i ogólnie wywoływać na nich metody. Dzięki obsłudze niejawnych parametrów staje się bardzo potężnym narzędziem. Weźmy na przykład następujący przykład:
Podczas przechowywania elementu przechowujemy również „Manifest”. Manifest to klasa, której instancje reprezentują typy Scali. Te obiekty mają więcej informacji niż JVM, co pozwala nam przetestować pełny, sparametryzowany typ.
Zauważ jednak, że a
Manifest
jest wciąż rozwijającą się funkcją. Jako przykład swoich ograniczeń, obecnie nie wie nic o wariancji i zakłada, że wszystko jest wariantem alternatywnym. Oczekuję, że stanie się bardziej stabilny i solidny, gdy skończy się biblioteka refleksji Scala, która jest obecnie w fazie rozwoju.źródło
get
Sposób może być określony jakofor ((om, v) <- _map get key if om <:< m) yield v.asInstanceOf[T]
.TypeTag
rzeczywistości są one automatycznie używane przy dopasowywaniu wzorców? Fajnie, co?Manifest
sam parametr, patrz: stackoverflow.com/a/11495793/694469 „instancja [manifest / type-tag] [...] jest niejawnie tworzona przez kompilator „Możesz to zrobić za pomocą TypeTags (jak już wspomina Daniel, ale ja to wyraźnie przeliteruję):
Możesz to również zrobić za pomocą ClassTags (co oszczędza Ci konieczności polegania na scala-reflect):
ClassTags można stosować, o ile nie oczekuje się, że
A
sam parametr type będzie typem ogólnym.Niestety jest to trochę szczegółowe i potrzebujesz adnotacji @unchecked, aby ukryć ostrzeżenie kompilatora. W przyszłości TypeTag może zostać automatycznie włączony przez kompilator do dopasowania wzorca: https://issues.scala-lang.org/browse/SI-6517
źródło
[List String @unchecked]
ponieważ nie dodaje nic do tego wzorca dopasowania (samo użyciecase strlist if typeOf[A] =:= typeOf[String] =>
spowoduje to, a nawetcase _ if typeOf[A] =:= typeOf[String] =>
jeśli powiązana zmienna nie jest potrzebna w treścicase
).=>
zostanie wykonany. (A kiedy kod na rhs jest wykonywany, strażnicy zapewniają statyczną gwarancję na rodzaj elementów. Może tam być rzut, ale jest bezpieczny.)Możesz użyć
Typeable
klasy typu od bezkształtnego, aby uzyskać oczekiwany wynik,Przykładowa sesja REPL,
cast
Operacja nie jest tak precyzyjny wrt usunięcia możliwie biorąc pod uwagę należących do zakresuTypeable
przykłady dostępnych.źródło
l1.cast[List[String]]
znaczy z grubszafor (x<-l1) assert(x.isInstanceOf[String]
) W przypadku dużych struktur danych lub jeśli rzutowania zdarzają się bardzo często, może to być niedopuszczalne obciążenie ogólne.Wymyśliłem stosunkowo proste rozwiązanie, które wystarczyłoby w sytuacjach o ograniczonym użyciu, zasadniczo owijając sparametryzowane typy, które ucierpiałyby z powodu problemu z usunięciem typu w klasach opakowań, które mogą być użyte w instrukcji match.
Daje to oczekiwany wynik i ogranicza zawartość naszej klasy przypadków do pożądanego typu, Listy ciągów.
Więcej informacji tutaj: http://www.scalafied.com/?p=60
źródło
Istnieje sposób na rozwiązanie problemu usuwania typu w Scali. W opcji Pokonywanie wymazywania typu w dopasowaniu 1 i Pokonywanie wymazywania typu w dopasowywaniu 2 (wariancja) znajduje się wyjaśnienie, w jaki sposób zakodować niektóre pomocniki do zawijania typów, w tym wariancji, w celu dopasowania.
źródło
Znalazłem nieco lepsze obejście tego ograniczenia skądinąd niesamowitego języka.
W Scali problem usuwania typu nie występuje w przypadku tablic. Myślę, że łatwiej jest to zademonstrować na przykładzie.
Powiedzmy, że mamy listę
(Int, String)
, a następnie poniżej podano ostrzeżenie o usunięciu typuAby obejść ten problem, najpierw utwórz klasę sprawy:
następnie we wzorcu dopasowania wykonaj coś takiego:
który wydaje się działać idealnie.
Będzie to wymagało drobnych zmian w kodzie do pracy z tablicami zamiast listami, ale nie powinno to stanowić poważnego problemu.
Zauważ, że użycie
case a:Array[(Int, String)]
nadal da ostrzeżenie o usunięciu typu, dlatego konieczne jest użycie nowej klasy kontenera (w tym przykładzieIntString
).źródło
Ponieważ Java nie zna faktycznego typu elementu, uznałem, że najbardziej użyteczne jest po prostu użycie
List[_]
. Potem ostrzeżenie znika, a kod opisuje rzeczywistość - jest to lista czegoś nieznanego.źródło
Zastanawiam się, czy jest to odpowiednie obejście:
Nie pasuje do „pustej listy”, ale daje błąd kompilacji, a nie ostrzeżenie!
To z drugiej strony wydaje się działać ....
Czy to nie jest nawet lepsze, czy nie rozumiem tutaj tego?
źródło
Nie rozwiązanie, ale sposób na życie z nim bez zamiatania go całkowicie pod dywan: dodawanie
@unchecked
adnotacji. Zobacz tutaj - http://www.scala-lang.org/api/current/index.html#scala.uncheckedźródło
Chciałem dodać odpowiedź, która uogólnia problem: Jak uzyskać ciąg reprezentujący typ mojej listy w czasie wykonywania
źródło
Korzystanie ze wzoru dopasowania dopasowania
źródło
isInstanceOf
sprawdzenie środowiska wykonawczego na podstawie informacji o typie dostępnych dla JVM. I te informacje o środowisku wykonawczym nie będą zawierać argumentu typu doList
(z powodu usunięcia typu).