Przechodziłem przez samouczek Scala playframework i natrafiłem na ten fragment kodu, który mnie zastanawiał:
def newTask = Action { implicit request =>
taskForm.bindFromRequest.fold(
errors => BadRequest(views.html.index(Task.all(), errors)),
label => {
Task.create(label)
Redirect(routes.Application.tasks())
}
)
}
Postanowiłem więc to zbadać i natknąłem się na ten post .
Nadal nie rozumiem.
Jaka jest różnica między tym:
implicit def double2Int(d : Double) : Int = d.toInt
i
def double2IntNonImplicit(d : Double) : Int = d.toInt
poza oczywistym faktem, że mają różne nazwy metod.
Kiedy powinienem używać implicit
i dlaczego?
scala
syntax
playframework
keyword
Clive
źródło
źródło
Odpowiedzi:
Poniżej wyjaśnię główne przypadki zastosowania implicitów, ale w celu uzyskania bardziej szczegółowych informacji patrz odpowiedni rozdział Programowanie w Scali .
Domniemane parametry
Końcową listę parametrów w metodzie można zaznaczyć
implicit
, co oznacza, że wartości zostaną pobrane z kontekstu, w którym są wywoływane. Jeśli nie ma niejawnej wartości odpowiedniego typu w zakresie, nie zostanie skompilowana. Ponieważ wartość niejawna musi zostać rozpoznana jako jedna wartość i aby uniknąć kolizji, dobrym pomysłem jest uczynienie typu specyficznym dla jego celu, np. Nie wymagaj od swoich metod szukania wartości niejawnejInt
!przykład:
Niejawne konwersje
Gdy kompilator znajdzie wyrażenie niewłaściwego typu dla kontekstu, będzie szukał niejawnej
Function
wartości typu, która pozwoli mu sprawdzić typ. Jeśli więcA
jest wymagane i znajdzie aB
, to będzie szukało wartości domyślnej typuB => A
w zakresie (sprawdza także inne miejsca, takie jak w obiektachB
iA
obiektach towarzyszących, jeśli takie istnieją). Ponieważdef
s można „rozszerzyć eta” naFunction
obiekty, animplicit def xyz(arg: B): A
zrobi.Różnica między twoimi metodami polega na tym, że ta zaznaczona
implicit
zostanie wstawiona przez kompilator, gdyDouble
zostanie znaleziony a, aleInt
jest wymagany.będzie działać tak samo jak
W drugiej wstawiliśmy konwersję ręcznie; w pierwszym kompilator zrobił to samo automatycznie. Konwersja jest wymagana ze względu na adnotację typu po lewej stronie.
Jeśli chodzi o Twój pierwszy fragment z Play:
Działania zostały wyjaśnione na tej stronie w dokumentacji Play (zobacz także dokumenty API ). Ty używasz
na
Action
obiekcie (który jest towarzyszem cechy o tej samej nazwie).Musimy więc podać funkcję jako argument, który można zapisać w postaci literału
W literale funkcyjnym część przed
=>
znakiem jest deklaracją wartości i może być oznaczona,implicit
jeśli chcesz, tak jak w każdej innejval
deklaracji. W tym przypadkurequest
nie trzeba zaznaczaćimplicit
tej opcji w celu sprawdzenia typu, ale dzięki temu będzie ona dostępna jako wartość domyślna dla wszelkich metod, które mogą jej potrzebować w ramach funkcji (i oczywiście można jej również jawnie użyć) . W tym konkretnym przypadku zostało to zrobione, ponieważbindFromRequest
metoda klasy Form wymaga niejawnegoRequest
argumentu.źródło
UWAGA: zawiera sarkazm rozsądnie! YMMV ...
Odpowiedź Luigiego jest kompletna i poprawna. Ten jest tylko trochę rozszerzony o przykład, jak możesz chwalebnie nadużywać implicytów , jak to często zdarza się w projektach Scala. W rzeczywistości tak często można go nawet znaleźć w jednym z przewodników „najlepszych praktyk” .
źródło
Dlaczego i kiedy należy oznaczyć
request
parametr jakoimplicit
:Niektóre metody, których użyjesz w treści działania, mają niejawną listę parametrów , na przykład Form.scala definiuje metodę:
Niekoniecznie zauważasz to, jak byś po prostu zadzwonił.
myForm.bindFromRequest()
Nie musisz jawnie podawać ukrytych argumentów. Nie, pozostawiasz kompilatorowi, aby szukał dowolnego poprawnego obiektu kandydującego do przekazania za każdym razem, gdy natrafi na wywołanie metody wymagające instancji żądania. Ponieważ zrobić mieć żądanie dostępne, wszystko co musisz zrobić, to zaznaczyć go jakoimplicit
.Użytkownik wyraźnie oznaczy go jako dostępny do niejawnego użytku.
Podpowiedziałeś kompilatorowi, że „OK” może używać obiektu żądania przesłanego przez środowisko Play (że nadaliśmy nazwę „żądanie”, ale mogliśmy użyć tylko „r” lub „req”) wszędzie tam, gdzie było to wymagane, „podstępnie” .
Zobacz to? nie ma go, ale jest tam !
To właśnie dzieje się bez twojej konieczności włożenia go ręcznie w każdym miejscu jest to potrzebne (ale może przekazać je wyraźnie, jeśli sobie tego życzenia, bez względu na to czy jest to zaznaczone
implicit
lub nie):Bez oznakowania go jako niejawny, byś musiał zrobić powyższych. Oznaczając to jako dorozumiane, nie musisz.
Kiedy należy oznaczyć wniosek jako
implicit
? Naprawdę potrzebujesz, jeśli korzystasz z metod, które deklarują niejawną listę parametrów oczekującą wystąpienia żądania . Ale dla uproszczenia możesz po prostu nabrać zwyczaju zaznaczania żądaniaimplicit
zawsze . W ten sposób możesz po prostu napisać piękny zwięzły kod.źródło
W scala niejawne działa jako :
Przetwornik
Wtryskiwacz wartości parametru
Istnieją 3 rodzaje zastosowania Implicit
Konwersja typu niejawnie : Konwertuje błąd powodujący przypisanie błędu na zamierzony typ
val x: String = "1"
val y: Int = x
String nie jest podtypem Int , więc błąd występuje w wierszu 2. Aby rozwiązać błąd, kompilator wyszuka taką metodę w zakresie, który zawiera niejawne słowo kluczowe i przyjmuje String jako argument i zwraca Int .
więc
Niejawna konwersja odbiornika : generalnie na podstawie właściwości obiektu wywołania odbiorcy, np. metody lub zmienne. Tak więc, aby wywołać dowolną właściwość przez odbiorcę, właściwość musi być członkiem klasy / obiektu tego odbiorcy.
Tutaj mahadi.haveTv spowoduje błąd. Ponieważ kompilator Scala najpierw poszuka właściwości haveTv w odbiorniku Mahadi . Nie znajdzie. Po drugie, będzie szukał metody o zasięgu z niejawnym słowem kluczowym, która przyjmuje obiekt Mahadi jako argument i zwraca obiekt Johnny . Ale tu nie ma. To spowoduje błąd . Ale następujące jest w porządku.
Niejawne wprowadzanie parametrów : jeśli wywołamy metodę i nie przekażemy jej wartości parametru, spowoduje to błąd. Kompilator scala działa w ten sposób - najpierw spróbuje przekazać wartość, ale nie otrzyma bezpośredniej wartości parametru.
Po drugie, jeśli parametr ma żadnych niejawny słowa kluczowego będzie wyglądać za każdym val w zakresie , które mają ten sam typ wartości. Jeśli nie dostaniesz spowoduje błąd.
Aby spowolnić ten problem, kompilator szuka niejawnej wartości typu Int, ponieważ parametr a ma niejawne słowo kluczowe .
Inny przykład:
możemy też napisać jak
Ponieważ l ma niejawny parametr oraz w zakresie treści metody x , istnieje niejawna zmienna lokalna ( parametry to zmienne lokalne ) a, która jest parametrem x , więc w treści metody x wartość domyślnego argumentu l podpisu metody wynosi złożony przez lokalną zmienną niejawny x metody badaniem (parametrów)
a
w sposób dorozumiany .Więc
będzie w takim kompilatorze
Inny przykład:
może to być przyczyną błędów, ponieważ C w X = {x> c} potrzebuje wyraźnie wartości pominięciem argument lub utajonego Val zakres .
Możemy więc uczynić parametr literału funkcji jawnie niejawnym, gdy wywołujemy metodę x
Zostało to wykorzystane w metodzie akcji Play-Framework
jeśli nie wymieniasz jawnie parametru żądania jako jawnego, to musisz mieć napisane-
źródło
Ponadto w powyższym przypadku powinna istnieć
only one
funkcja niejawna, której typ jestdouble => Int
. W przeciwnym razie kompilator zostanie zdezorientowany i nie będzie się poprawnie kompilował.źródło
Bardzo prosty przykład Implicits in scala.
Domniemane parametry :
Uwaga: Tutaj
multiplier
zostanie domyślnie przekazane do funkcjimultiply
. Brakujące parametry wywołania funkcji są wyszukiwane według typu w bieżącym zakresie, co oznacza, że kod nie zostanie skompilowany, jeśli w zasięgu nie będzie niejawnej zmiennej typu Int.Niejawne konwersje :
Uwaga: Kiedy wywołujemy
multiply
funkcję przekazującą podwójną wartość, kompilator spróbuje znaleźć funkcję niejawną konwersji w bieżącym zakresie, który konwertujeInt
naDouble
(Jako parametrmultiply
akceptujący funkcjęInt
). Jeśli nie ma niejawnejconvert
funkcji, kompilator nie skompiluje kodu.źródło
Miałem dokładnie to samo pytanie, co ty i myślę, że powinienem podzielić się tym, jak zacząłem to rozumieć, poprzez kilka naprawdę prostych przykładów (zauważ, że obejmuje on tylko typowe przypadki użycia).
Istnieją dwa typowe przypadki użycia przy użyciu Scali
implicit
.Przykłady są następujące
Używanie go w zmiennej . Jak widać, jeśli
implicit
słowo kluczowe zostanie użyte na liście ostatnich parametrów, zostanie użyta najbliższa zmienna.Używanie go w funkcji . Jak widać, jeśli
implicit
zostanie użyta w funkcji, zastosowana zostanie metoda konwersji typu najbliższego.Mam nadzieję, że to może pomóc.
źródło