2015-12-27 11 views
5

Grałem dzisiaj z Javą i zauważyłem coś dziwnego. Rozważmy następujący kod:Metoda odlewania Java nie wiedząc, co przesyłać do

String foo = cast("hi"); 
int bar = cast("1"); 

Sposób cast() jest tutaj:

public static <T> T cast(Object value) { 
    return (T) value; 
} 

Java wydaje się rzucać "hi" do String, nawet jeśli nie przechodzą żadnego śladu, że byłoby to String, z wyjątkiem typu. Dobrze działa pod numerem foo, ale kończy się niepowodzeniem na bar, ponieważ nie można odwzorować ciągu znaków na liczbę całkowitą. Co tu się dzieje?

Mam dwa przypuszczenia:

  1. Sposób obsada jest zwrócenie Object, a przy inicjalizacji automatycznie rzuca do rodzaju.

    To nie ma sensu, gdyż dają mi:

    Type mismatch: cannot convert from Object to int 
    
  2. Java wydaje się wiedzieć, że musi oddać do String, lub niezależnie od rodzaju zmienną jest. Ale jak to działa?

+0

@KLibby Zostało to zadeklarowane w metodzie. Mówi "public static T cast (wartość obiektu) {'. Zwróć uwagę na ''. – TheCoffeeCup

+0

Deklaracja klasy jest po prostu "publicznym testem klasy". – TheCoffeeCup

+0

Drugi daje mi wyjątek środowiska wykonawczego, a nie błąd czasu kompilacji. Czy o tym mówisz? – markspace

Odpowiedz

6

Twoja metoda rzutowania to unchecked conversion, obsługiwana specjalnie w JVM w celu zachowania zgodności wstecz z nietypowym kodem.

Nie można wykazać, że takie połączenia nie są statycznie bezpieczne w systemie typu przy użyciu generycznych. Odrzucenie takich wywołań spowoduje unieważnienie dużych ciał istniejącego kodu i uniemożliwi korzystanie z nowszych wersji bibliotek. JLS 5.1.9

Wywołanie metody bez parametrów typu wyraźnych spowoduje kompilator notowanym parametr rodzaj inwokacji, w tym przypadku na podstawie przewidywanego rodzaju powrotnego. Type Inference, JLS 15.12.2.7.. Oznacza to, że kod jest equvivalent do tego:

String foo = Caster.<String>cast("hi"); // no exception 
int bar = Caster.<Integer>cast("1"); // runtime ClassCastException 

typie pierwotnym będzie wnioskować do ich pudełkowej wersji:

Jeżeli A jest prymitywny typ, to A jest konwertowany do typu odniesienia U poprzez konwersję boksu i ten algorytm jest stosowana rekurencyjnie do ograniczenia U < < F. JLS 15.12.2.7.

JVM zapewnia Typ bezpieczeństwa wykonując kontrole typu wykonawczego na wartości zwracane funkcji CO Pobieranie niezaznaczonych rzutów, w pierwszym punkcie, w którym informacja o typie nie została usunięta (nie znalazłem tego wyraźnie w specyfikacji, ale rzeczy wyglądają tak, aby działać w ten sposób, chociaż jest wspomniany w The Java Tutorials). W tym przypadku, gdy spróbujesz przypisać wartość do wpisanej zmiennej lokalnej, sprawdzany jest typ zwracanej wartości, co daje ClassCastException.

Aby dać trochę więcej pojęcia, kiedy to egzekwowane Runtime typu sprawdzanie obsada dzieje, oto kilka innych przykładów:

Object a3 = Caster.<String>cast(3); // no exception, a3 is now Integer 
Object a4 = (String)Caster.<String>cast(3); // an explicit cast causes runtime ClassCastException 

EDIT:

Oto StackOverflow pytanie o to, kiedy są typu Runtime kontrole zostały wykonane: When is generic return value of function casted after type erasure?

7
public static <T> T cast(Object value) { 
    return (T) value; 
} 

Ważną częścią jest <T> na metodzie, oznacza to, że metoda zwróci typu w oparciu o to, co wywołanie metody spodziewać.

String foo = cast("hi"); // left side is a String, <T> will be typed as String 


Drugi jest nieco bardziej skomplikowany, ponieważ typy generyczne nie mogą być prymitywne typy. Domyślam się, że zwraca najczęściej typ Object.

int bar = cast("1"); // left side is primitive 
        // <T> will be typed by with the corresponding non-primitive type. 
        // But it fails because "1" cannot be casted to java.lang.Integer 
+1

Myślę, że dla 'int', że faktycznie jest rzutowany na Integer. To jest komunikat o błędzie, który otrzymuję: 'java.lang.ClassCastException: java.lang.String nie może być rzutowany na java.lang.Integer' – markspace

+0

tak, ładna magia w powietrzu :) ok, prymitywy wydają się być zastąpione przez klasy pierwotne. @markspace thansk, naprawiłem mój błąd. –

+0

@AnthonyRaymond Nazywa się auto-unboxing –

0

Nie widzę żadnego dziwnego zachowania.

w:

String foo = cast("hi"); 

"hi" jest Object (jak wszystko w Javie), więc byłoby zaakceptowane przez kompilator jako Object argument. Po wywołaniu funkcja cast() zwraca "hi", która jest łańcuchem i jest przypisana do String foo. Nie ma tu dziwnego zachowania.

w:

int bar = cast("1"); 

"1" jest obiektem, i to jest akceptowane przez kompilator. Funkcja cast zwraca "1", czyli ciąg znaków. W przeciwieństwie do innych języków (np. JavaScript), ciąg nie może być bezpośrednio rzutowany jako liczba całkowita. Nie ma tu dziwnego zachowania.

Jeżeli zmienisz funkcję cast() do:

public static <T> T cast(T value) { 
    return value; 
} 

Dostaniesz błąd w czasie kompilacji, zamiast czasu realizacji.