Czy ta funkcjonalność zostanie wprowadzona w późniejszej wersji Java?
Czy ktoś może wyjaśnić, dlaczego nie mogę tego zrobić, tak jak w technicznym sposobie działania switch
instrukcji Java ?
java
string
switch-statement
Alex Beardsley
źródło
źródło
"Don't hold your breath."
lol, bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179Odpowiedzi:
Deklaracje zamiany z
String
przypadkami zostały zaimplementowane w Javie SE 7 , co najmniej 16 lat po ich pierwszym zgłoszeniu. Nie podano wyraźnego powodu opóźnienia, ale prawdopodobnie miało to związek z wydajnością.Wdrożenie w JDK 7
Funkcja została teraz zaimplementowana w
javac
procesie „usuwania cukru”; czysta, wysokopoziomowa składnia wykorzystującaString
stałe wcase
deklaracjach jest rozszerzana w czasie kompilacji do bardziej złożonego kodu zgodnie ze wzorcem. Wynikowy kod używa instrukcji JVM, które zawsze istniały.A
switch
zString
przypadkami jest tłumaczone na dwa przełączniki podczas kompilacji. Pierwszy mapuje każdy ciąg na unikalną liczbę całkowitą - jego pozycję w oryginalnym przełączniku. Odbywa się to poprzez pierwsze włączenie kodu skrótu etykiety. Odpowiednim przypadkiem jestif
instrukcja, która testuje równość łańcucha; jeśli w haszu występują kolizje, test jest kaskadowyif-else-if
. Drugi przełącznik odzwierciedla to w oryginalnym kodzie źródłowym, ale zastępuje etykiety liter odpowiednimi pozycjami. Ten dwuetapowy proces ułatwia zachowanie kontroli przepływu oryginalnego przełącznika.Przełączniki w JVM
Aby uzyskać więcej szczegółów technicznych
switch
, można zapoznać się ze specyfikacją JVM, w której opisano kompilację instrukcji switch . Krótko mówiąc, istnieją dwie różne instrukcje JVM, których można użyć dla przełącznika, w zależności od rzadkości stałych używanych przez przypadki. Oba zależą od wykorzystania stałych liczb całkowitych dla każdego przypadku, aby wykonać je skutecznie.Jeśli stałe są gęste, są one używane jako indeks (po odjęciu najniższej wartości) do tabeli wskaźników instrukcji -
tableswitch
instrukcji.Jeśli stałe są rzadkie, przeprowadzane jest binarne wyszukiwanie poprawnej wielkości liter -
lookupswitch
instrukcja.W odcukrzania
switch
naString
obiektach, obie instrukcje mogą być użyte.lookupswitch
Nadaje pierwszy przełącznik na kody hash znaleźć oryginalne stanowisko w sprawie. Wynikowy porządek jest naturalnym dopasowaniem dotableswitch
.Obie instrukcje wymagają sortowania stałych liczb całkowitych przypisanych do każdego przypadku w czasie kompilacji. W czasie wykonywania
O(1)
wydajność natableswitch
ogół wydaje się lepsza niżO(log(n))
wydajnośćlookupswitch
, ale wymaga pewnej analizy w celu ustalenia, czy tabela jest wystarczająco gęsta, aby uzasadnić kompromis czasoprzestrzenny. Bill Venners napisał świetny artykuł, który omawia to bardziej szczegółowo, wraz z ukrytym spojrzeniem na inne instrukcje kontroli przepływu Java.Przed JDK 7
Przed JDK 7
enum
mógł w przybliżeniuString
przełącznik oparty na przełączniku. Wykorzystuje statycznąvalueOf
metodę generowaną przez kompilator dla każdegoenum
typu. Na przykład:źródło
Pill
do podjęcia pewnych działań w oparciu ostr
, argumentowałbym, czy preferowane jest użycie opcji else-else, ponieważ pozwala ona obsługiwaćstr
wartości spoza zakresu CZERWONEGO, NIEBIESKIEGO bez potrzeby wychwytywania wyjątkuvalueOf
lub ręcznego sprawdzania zgodności z nazwą każdy typ wyliczenia, który po prostu dodaje niepotrzebne koszty ogólne. Z mojego doświadczenia wynika, żevalueOf
przekształcenie w wyliczenie ma sens tylko wtedy, gdy później potrzebna będzie bezpieczna reprezentacja wartości ciągu.(hash >> x) & ((1<<y)-1)
dałby różne wartości dla każdego łańcucha, któryhashCode
jest inny i(1<<y)
jest mniejszy niż dwukrotność liczby łańcuchów (lub w przynajmniej niewiele więcej niż to).Jeśli masz w kodzie miejsce, w którym możesz włączyć String, może być lepiej refaktoryzować String, aby był wyliczeniem możliwych wartości, które możesz włączyć. Oczywiście ograniczasz potencjalne wartości Ciągów, które możesz mieć, do tych z wyliczenia, które mogą być pożądane lub nie.
Oczywiście twoje wyliczenie może mieć wpis dla „other” oraz metodę fromString (String), wtedy możesz mieć
źródło
Poniżej znajduje się kompletny przykład oparty na poście JeeBee, w którym zastosowano enum java zamiast metody niestandardowej.
Zauważ, że w Javie SE 7 i nowszych można użyć obiektu String w wyrażeniu instrukcji switch.
źródło
Przełączniki oparte na liczbach całkowitych można zoptymalizować pod kątem bardzo skutecznego kodu. Przełączniki oparte na innym typie danych można skompilować tylko do szeregu instrukcji if ().
Z tego powodu C & C ++ zezwala tylko na przełączniki na liczbach całkowitych, ponieważ nie miało to sensu w przypadku innych typów.
Projektanci C # zdecydowali, że styl jest ważny, nawet jeśli nie ma żadnej przewagi.
Projektanci Javy najwyraźniej myśleli jak projektanci C.
źródło
Przykład bezpośredniego
String
użycia od wersji 1.7 może być również pokazany:źródło
James Curran zwięźle mówi: „Przełączniki oparte na liczbach całkowitych można zoptymalizować pod kątem bardzo skutecznego kodu. Przełączniki oparte na innym typie danych można skompilować tylko do szeregu instrukcji if (). Z tego powodu C i C ++ zezwalają tylko na przełączniki na liczbach całkowitych, ponieważ nie miało to sensu w przypadku innych typów ”.
Moim zdaniem i tylko tyle, że jak tylko zaczniesz włączać nie-prymitywy, musisz zacząć myśleć o „równa się” w porównaniu z „==”. Po pierwsze, porównanie dwóch ciągów może być dość długą procedurą, zwiększając problemy z wydajnością, o których mowa powyżej. Po drugie, jeśli istnieje włączanie ciągów, będzie zapotrzebowanie na włączenie ciągów ignorujących wielkość liter, włączenie ciągów z uwzględnieniem / ignorowanie ustawień regionalnych, włączenie ciągów opartych na wyrażeniu regularnym ... Chciałbym zatwierdzić decyzję, która zaoszczędziłaby dużo czasu na programistów języków kosztem niewielkiej ilości czasu dla programistów.
źródło
matched
inot matched
. (Nie biorąc jednak pod uwagę takich rzeczy, jak [nazwane] grupy / itp.)Oprócz powyższych dobrych argumentów dodam, że wielu ludzi postrzega dziś
switch
jako przestarzałą pozostałość proceduralnej przeszłości Jawy (do czasów C).Nie podzielam w pełni tej opinii, myślę, że
switch
może być przydatna w niektórych przypadkach, przynajmniej ze względu na jej szybkość, a poza tym jest lepsza niż pewna seria kaskadowych liczb,else if
które widziałem w jakimś kodzie ...Ale rzeczywiście warto spojrzeć na przypadek, w którym potrzebujesz przełącznika, i sprawdź, czy nie można go zastąpić czymś więcej OO. Na przykład wyliczenia w Javie 1.5+, być może HashTable lub innej kolekcji (czasami żałuję, że nie mamy (anonimowych) funkcji jako obywatele pierwszej klasy, jak w Lua - która nie ma przełącznika - lub JavaScript), a nawet polimorfizmu.
źródło
Jeśli nie używasz JDK7 lub nowszej wersji, możesz użyć jej
hashCode()
do symulacji. PonieważString.hashCode()
zwykle zwraca różne wartości dla różnych ciągów i zawsze zwraca równe wartości dla równych ciągów, jest dość wiarygodny (Różne ciągi mogą wytwarzać ten sam kod skrótu, jak @Lii wspomniany w komentarzu, taki jak"FB"
i"Ea"
) Zobacz dokumentację .Tak więc kod wyglądałby tak:
W ten sposób technicznie włączasz
int
.Alternatywnie możesz użyć następującego kodu:
źródło
case
sądzę, stwierdzenia, które zawsze muszą być stałymi wartościami, iString.hashCode()
takie nie są (nawet jeśli w praktyce obliczenia nigdy nie zmieniły się między JVM).case
wartości instrukcji nie muszą być możliwe do ustalenia w czasie kompilacji, więc działają dobrze.Od lat używamy do tego preprocesora (open source).
Wstępnie przetworzone pliki są nazywane Foo.jpp i przetwarzane do Foo.java za pomocą skryptu ant.
Zaletą jest to, że jest przetwarzany na Javę działającą w wersji 1.0 (chociaż zwykle obsługiwaliśmy tylko powrót do wersji 1.4). Znacznie łatwiej było to zrobić (wiele przełączników łańcuchowych) w porównaniu do przerabiania go za pomocą wyliczeń lub innych obejść - kod był o wiele łatwiejszy do odczytania, utrzymania i zrozumienia. IIRC (w tym momencie nie może dostarczyć statystyk ani uzasadnienia technicznego) było także szybsze niż naturalne odpowiedniki Javy.
Wady polegają na tym, że nie edytujesz Javy, więc jest to nieco większy przepływ pracy (edycja, przetwarzanie, kompilacja / test), a IDE połączy się ponownie z Javą, która jest nieco skomplikowana (zmiana staje się serią logicznych kroków if / else) a kolejność skrzynki przełączników nie jest zachowana.
Nie polecałbym tego dla wersji 1.7+, ale jest przydatny, jeśli chcesz programować Javę, która jest ukierunkowana na wcześniejsze maszyny JVM (ponieważ Joe public rzadko ma najnowszą zainstalowaną wersję).
Możesz go pobrać z SVN lub przeglądać kod online . Będziesz potrzebował EBuild, aby zbudować go takim, jakim jest.
źródło
Inne odpowiedzi mówiły, że zostało to dodane w Javie 7 i podano obejścia dla wcześniejszych wersji. Ta odpowiedź próbuje odpowiedzieć na pytanie „dlaczego”
Java była reakcją na nadmierną złożoność C ++. Został zaprojektowany jako prosty, czysty język.
String zyskał trochę specjalnej obsługi skrzynek w tym języku, ale wydaje mi się jasne, że projektanci starali się ograniczyć ilość specjalnej obudowy i cukru syntaktycznego do minimum.
włączanie łańcuchów jest dość skomplikowane pod maską, ponieważ łańcuchy nie są prostymi typami prymitywnymi. Nie była to powszechna funkcja w czasie projektowania Java i tak naprawdę nie pasuje do minimalistycznego wyglądu. Zwłaszcza, że zdecydowali się nie używać specjalnego case == dla ciągów, byłoby (i jest) trochę dziwne, aby case działał tam, gdzie == nie.
Między 1.0 a 1.4 sam język pozostał prawie taki sam. Większość ulepszeń w Javie była po stronie biblioteki.
Wszystko zmieniło się wraz z Javą 5, język został znacznie rozszerzony. Kolejne rozszerzenia pojawiły się w wersjach 7 i 8. Oczekuję, że ta zmiana nastawienia była spowodowana wzrostem C #
źródło
JEP 354: Przełącz wyrażenia ( wersja zapoznawcza) w JDK-13 i JEP 361: Przełącz wyrażenia (Standard) w JDK-14 rozszerzy instrukcję przełączania, aby mogła być używana jako wyrażenie .
Teraz możesz:
case L ->
):Demo z odpowiedzi ( 1 , 2 ) może wyglądać następująco:
źródło
Niezbyt ładna, ale oto inny sposób dla Java 6 i poniżej:
źródło
W Groovy to bryza; Osadzam groovy jar i tworzę
groovy
klasę narzędziową do robienia wszystkich tych rzeczy i więcej, co jest dla mnie irytujące w Javie (ponieważ utknąłem przy użyciu Java 6 w przedsiębiorstwie).źródło
Kiedy używasz intellij, spójrz również na:
Plik -> Struktura projektu -> Projekt
Plik -> Struktura projektu -> Moduły
Jeśli masz wiele modułów, upewnij się, że ustawiłeś właściwy poziom języka na karcie modułu.
źródło
źródło