Podczas moich dni w C ++ uczono mnie o złu operatora rzutowania w stylu C, na początku z przyjemnością odkryłem, że w Javie 5 java.lang.Class
nabyłem cast
metodę.
Pomyślałem, że wreszcie mamy OO sposób radzenia sobie z castingami.
Okazuje się, że Class.cast
to nie to samo, co static_cast
w C ++. To jest bardziej jak reinterpret_cast
. Nie wygeneruje błędu kompilacji tam, gdzie jest oczekiwany, a zamiast tego opóźni działanie. Oto prosty przypadek testowy pokazujący różne zachowania.
package test;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class TestCast
{
static final class Foo
{
}
static class Bar
{
}
static final class BarSubclass
extends Bar
{
}
@Test
public void test ( )
{
final Foo foo = new Foo( );
final Bar bar = new Bar( );
final BarSubclass bar_subclass = new BarSubclass( );
{
final Bar bar_ref = bar;
}
{
// Compilation error
final Bar bar_ref = foo;
}
{
// Compilation error
final Bar bar_ref = (Bar) foo;
}
try
{
// !!! Compiles fine, runtime exception
Bar.class.cast( foo );
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
{
final Bar bar_ref = bar_subclass;
}
try
{
// Compiles fine, runtime exception, equivalent of C++ dynamic_cast
final BarSubclass bar_subclass_ref = (BarSubclass) bar;
}
catch ( final ClassCastException ex )
{
assertTrue( true );
}
}
}
Więc to są moje pytania.
- Powinien
Class.cast()
zostać wygnany do ziemi Generics? Tam ma sporo legalnych zastosowań. - Czy kompilatory powinny generować błędy kompilacji, gdy
Class.cast()
są używane i można określić niedozwolone warunki w czasie kompilacji? - Czy Java powinna udostępniać operator rzutowania jako konstrukcję języka podobną do C ++?
java
generics
casting
compiler-warnings
Alexander Pogrebnyak
źródło
źródło
Class.cast()
gdy w czasie kompilacji można określić niedozwolone warunki. W takim przypadku wszyscy oprócz Ciebie używają tylko standardowego operatora rzutowania. (3) Java ma operator rzutowania jako konstrukcję językową. Nie jest podobny do C ++. Dzieje się tak, ponieważ wiele konstrukcji języka Java nie jest podobnych do C ++. Pomimo powierzchownych podobieństw, Java i C ++ są zupełnie inne.Odpowiedzi:
Zawsze starałem
Class.cast(Object)
się unikać ostrzeżeń w „krainie leków generycznych”. Często widzę metody, które robią takie rzeczy:Często najlepiej jest go zastąpić:
To jedyny przypadek użycia, z
Class.cast(Object)
jakim kiedykolwiek się spotkałem.Odnośnie ostrzeżeń kompilatora: Podejrzewam, że
Class.cast(Object)
nie jest to szczególne dla kompilatora. Można go zoptymalizować, gdy jest używany statycznie (tj.Foo.class.cast(o)
Zamiastcls.cast(o)
), ale nigdy nie widziałem nikogo, kto go używa - co sprawia, że wysiłek związany z wbudowaniem tej optymalizacji w kompilator jest nieco bezwartościowy.źródło
Po pierwsze, zdecydowanie odradza się wykonywanie prawie każdego rzutu, więc powinieneś go jak najbardziej ograniczyć! Tracisz korzyści płynące z silnie wpisywanych w czasie kompilacji funkcji języka Java.
W każdym razie
Class.cast()
powinno być używane głównie podczas pobieraniaClass
tokena przez odbicie. Pisanie jest bardziej idiomatycznezamiast
EDYCJA: błędy w czasie kompilacji
Ogólnie rzecz biorąc, Java sprawdza rzutowanie tylko w czasie wykonywania. Jednak kompilator może zgłosić błąd, jeśli może udowodnić, że takie rzutowanie nigdy się nie powiedzie (np. Rzutowanie klasy na inną klasę, która nie jest nadtypem i rzutowanie ostatecznego typu klasy na klasę / interfejs, który nie znajduje się w jego hierarchii typów). Tutaj od
Foo
iBar
są klasy, które nie są w każdej innej hierarchii, obsada nie może się udać.źródło
Class.cast
wydaje się, że pasuje do ustawy, stwarza więcej problemów niż rozwiązuje.Próba tłumaczenia konstrukcji i pojęć między językami jest zawsze problematyczna i często wprowadza w błąd. Casting nie jest wyjątkiem. Szczególnie dlatego, że Java jest językiem dynamicznym, a C ++ jest nieco inny.
Całe przesyłanie w Javie, bez względu na to, jak to robisz, odbywa się w czasie wykonywania. Informacje o typie są przechowywane w czasie wykonywania. C ++ to trochę więcej mieszanka. Możesz rzutować strukturę w C ++ na inną i jest to po prostu reinterpretacja bajtów, które reprezentują te struktury. Java nie działa w ten sposób.
Również typy generyczne w Javie i C ++ są bardzo różne. Nie przejmuj się zbytnio tym, jak robisz rzeczy w C ++ w Javie. Musisz nauczyć się robić rzeczy na sposób Java.
źródło
(Bar) foo
, generuje błąd w czasie kompilacji, aleBar.class.cast(foo)
tak nie jest. Moim zdaniem, jeśli jest używany w ten sposób, powinien.Bar.class.cast(foo)
jawnie informuje kompilator, że chcesz wykonać rzutowanie w czasie wykonywania. Jeśli chcesz sprawdzić w czasie kompilacji poprawność rzutowania, jedynym wyborem jest wykonanie(Bar) foo
rzutowania stylu.Class.cast()
jest rzadko używany w kodzie Java. Jeśli jest używany, to zwykle z typami, które są znane tylko w czasie wykonywania (tj. Przez ich odpowiednieClass
obiekty i przez jakiś parametr typu). Jest naprawdę przydatny tylko w kodzie, który używa typów ogólnych (z tego powodu nie został wprowadzony wcześniej).To nie podobne do
reinterpret_cast
, ponieważ będzie to nie pozwalają przełamać system typu w czasie wykonywania więcej niż zwykły odlew ma (czyli może pan złamać parametry typu rodzajowego, ale nie może złamać „prawdziwe” typy).Zło operatora rzutowania w stylu C zazwyczaj nie dotyczy języka Java. Kod Java, który wygląda jak rzutowanie w stylu C, jest najbardziej podobny do kodu
dynamic_cast<>()
z typem referencyjnym w Javie (pamiętaj: Java ma informacje o typie środowiska wykonawczego).Ogólnie porównywanie operatorów rzutowania w C ++ z rzutowaniem w Javie jest dość trudne, ponieważ w Javie można rzutować tylko referencje i nigdy nie zachodzi żadna konwersja obiektów (przy użyciu tej składni można konwertować tylko wartości pierwotne).
źródło
dynamic_cast<>()
z typem odniesienia.Ogólnie rzecz biorąc, operator rzutowania jest preferowany względem metody cast klasy #, ponieważ jest bardziej zwięzły i może zostać przeanalizowany przez kompilator w celu wyplucia rażących problemów z kodem.
Class # cast bierze odpowiedzialność za sprawdzanie typów w czasie wykonywania, a nie podczas kompilacji.
Z pewnością istnieją przypadki użycia rzutowania klasy #, szczególnie jeśli chodzi o operacje odblaskowe.
Odkąd lambda pojawiły się w Javie, osobiście lubię używać rzutowania klasy # z interfejsem API kolekcji / strumienia, jeśli pracuję na przykład z typami abstrakcyjnymi.
źródło
C ++ i Java to różne języki.
Operator rzutowania w stylu Java C jest znacznie bardziej ograniczony niż wersja C / C ++. W rzeczywistości rzutowanie Java jest podobne do C ++ dynamic_cast, jeśli obiekt, którego masz, nie może być rzutowany na nową klasę, otrzymasz wyjątek czasu wykonywania (lub jeśli jest wystarczająco dużo informacji w kodzie, czas kompilacji). Dlatego pomysł C ++, aby nie używać rzutów typu C, nie jest dobrym pomysłem w Javie
źródło
Oprócz usunięcia brzydkich ostrzeżeń o rzutach, jak większość wspomniano, Class.cast jest rzutowaniem w czasie wykonywania, używanym głównie z rzutowaniem ogólnym, ponieważ ogólne informacje zostaną usunięte w czasie wykonywania i niektóre, jak każdy rodzaj będzie traktowany jako Obiekt, co prowadzi do tego, że nie zgłoś wczesny wyjątek ClassCastException.
na przykład serviceLoder używa tej sztuczki podczas tworzenia obiektów, sprawdź S p = service.cast (c.newInstance ()); spowoduje to zgłoszenie wyjątku rzutowania klasy, gdy SP = (S) c.newInstance (); nie będzie i może wyświetlać ostrzeżenie „Bezpieczeństwo typów: niezaznaczone rzutowanie z obiektu do S” `` . (tak samo jak Object P = (Object) c.newInstance ();)
- po prostu sprawdza, czy rzutowany obiekt jest instancją klasy rzutującej, a następnie użyje operatora rzutowania, aby rzucić i ukryć ostrzeżenie, pomijając je.
implementacja java dla dynamicznego przesyłania:
źródło
Osobiście użyłem tego wcześniej do zbudowania konwertera JSON na POJO. W przypadku, gdy obiekt JSONObject przetwarzany za pomocą funkcji zawiera tablicę lub zagnieżdżone obiekty JSONObject (co oznacza, że dane tutaj nie są typu pierwotnego lub
String
), próbuję wywołać metodę ustawiającą, używającclass.cast()
w następujący sposób:Nie jestem pewien, czy jest to niezwykle pomocne, ale jak powiedziałem tutaj wcześniej, refleksja jest jednym z nielicznych uzasadnionych przypadków użycia,
class.cast()
które mogę wymyślić, przynajmniej masz teraz inny przykład.źródło