3 回答

TA貢獻1946條經驗 獲得超4個贊
引用vs對象vs類型
對我而言,關鍵是理解對象及其引用之間的區(qū)別,換句話說,就是對象及其類型之間的區(qū)別。
當我們用Java創(chuàng)建對象時,我們聲明了它的真實本質,它將永遠不會改變。但是Java中的任何給定對象都可能具有多種類型。這些類型中的某些顯然歸功于類層次結構,而其他類型則不是那么明顯(即泛型,數組)。
專門針對引用類型,類層次結構規(guī)定了子類型化規(guī)則。例如,在您的示例中,所有卡車均為重型車輛,所有重型車輛均為。因此,這種is-a關系層次結構指示卡車具有多種兼容類型。
創(chuàng)建時Truck,我們定義一個“引用”來訪問它。該引用必須具有這些兼容類型之一。
Truck t = new Truck(); //or
HeavyVehicle hv = new Truck(); //or
Vehicle h = new Truck() //or
Object o = new Truck();
因此,這里的關鍵是要認識到對對象的引用不是對象本身。創(chuàng)建的對象的性質永遠不會改變。但是我們可以使用各種兼容的引用來訪問該對象。這是這里多態(tài)性的特征之一。可以通過引用不同“兼容”類型的對象來訪問同一對象。
當我們進行任何類型的轉換時,我們只是假設不同類型的引用之間具有這種兼容性。
向上轉換或擴展參考轉換
現在,有了類型引用Truck,我們可以輕松得出結論,它始終與類型引用兼容Vehicle,因為所有卡車都是Vehicles。因此,我們可以不使用顯式強制轉換就向上引用該參考。
Truck t = new Truck();
Vehicle v = t;
這也稱為擴展引用轉換,基本上是因為當您進入類型層次結構時,類型會變得更加通用。
如果需要,可以在此處使用顯式轉換,但這不是必需的。我們可以看到t和所引用的實際對象v是相同的。是,并且將永遠是Truck。
向下轉換或縮小參考轉換
現在,有了類型的引用,Vechicle我們不能“安全地”得出結論,它實際上引用了Truck。畢竟,它也可以引用其他形式的車輛。例如
Vehicle v = new Sedan(); //a light vehicle
如果您v在代碼中的某處找到引用,卻不知道引用的是哪個特定對象,則不能“安全地”論證它是指向a Truck還是指向a Sedan或任何其他種類的車輛。
編譯器很清楚,它不能對所引用對象的真實性質提供任何保證。但是程序員通過閱讀代碼可以確定他/她正在做什么。像上述情況一樣,您可以清楚地看到它Vehicle v引用了Sedan。
在這些情況下,我們可以進行下調。之所以這樣稱呼,是因為我們要沿著類型層次結構前進。我們也稱此為縮窄參考轉換。我們可以說
Sedan s = (Sedan) v;
這總是需要顯式的強制轉換,因為編譯器不能確定這樣做是否安全,這就是為什么這就像問程序員“您確定自己在做什么嗎?”。如果您對編譯器撒謊,則ClassCastException在執(zhí)行此代碼時會在運行時得到。
其他種類的分型規(guī)則
Java中還有其他子類型化規(guī)則。例如,還有一個稱為數字提升的概念,它可以自動強制表達式中的數字。像
double d = 5 + 6.0;
在這種情況下,由兩種不同類型(整數和雙精度型)組成的表達式在評估該表達式之前將整數強制轉換/強制為雙精度型,從而產生雙精度值。
您也可以進行原始的向上轉換和向下轉換。如
int a = 10;
double b = a; //upcasting
int c = (int) b; //downcasting
在這些情況下,當信息可能丟失時,需要進行顯式轉換。
某些子類型化規(guī)則可能不那么明顯,例如在數組的情況下。例如,所有引用數組都是的子類型Object[],而原始數組則不是。
對于泛型,尤其是使用通配符(如super和)時extends,情況變得更加復雜。像
List<Integer> a = new ArrayList<>();
List<? extends Number> b = a;
List<Object> c = new ArrayList<>();
List<? super Number> d = c;
其中的類型b是的類型的子類型a。的類型d是的類型的子類型c。
裝箱和拆箱也受制于某些強制轉換規(guī)則(不過,在我看來,這也是一種強制形式)。

TA貢獻1777條經驗 獲得超10個贊
你答對了。您只能將對象成功地強制轉換為其類,其某些父類或該對象或其父級實現的某些接口。如果將其強制轉換為某些父類或接口,則可以將其強制轉換為原始類型。
否則(雖然可以在源代碼中使用它),它將導致運行時ClassCastException。
投射通常用于使在同一字段中存儲不同的東西(具有相同的接口或父類,例如您的所有汽車)或具有相同類型的集合(例如Vehicle)成為可能,以便您可以使用他們以同樣的方式。
如果您隨后希望獲得完全訪問權限,則可以將其撤回(例如,車輛到卡車)
在示例中,我非常確定最后一條語句無效,并且注釋完全錯誤。

TA貢獻1836條經驗 獲得超13個贊
最后一行代碼可以毫無例外地編譯并成功運行。它所做的是完全合法的。
hV最初是指類型為HeavyVehicle的對象(我們將此對象稱為h1):
static HeavyVehicle hV = new HeavyVehicle(); // hV now refers to h1.
稍后,我們將hV引用為卡車類型的另一個對象(我們將此對象稱為t1):
hV = T; // hV now refers to t1.
最后,我們使T參考t1。
T = (Truck) hV; // T now refers to t1.
T已經提到了t1,因此該語句沒有任何改變。
如果已經為hv分配了HeavyVehicle類的實例,該實例是Truck類的超類,那么如何將該字段類型轉換為一個更具體的子類,該子類從HeavyVehicle類擴展而來?
當我們到達最后一行時,hV不再引用HeavyVehicle的實例。它指的是Truck的一個實例。將Truck的實例強制轉換為Truck類型是沒有問題的。
這意味著該對象只能轉換為超類或與實際實例化該類的類相同的類。這是正確的還是我錯了?
基本上是,但是不要將對象本身與引用該對象的變量混淆。見下文。
顯式轉換是否會以某種方式更改對象的實際類型(而不僅僅是聲明的類型),從而使該對象不再是HeavyVehicle類的實例,而是現在成為Truck類的實例?
否。對象一旦創(chuàng)建,就無法更改其類型。它不能成為另一個類的實例。
重申一下,最后一行沒有任何變化。T在該行之前指的是t1,在其后指的是t1。
那么,為什么在最后一行必須進行顯式強制轉換(卡車)?我們基本上只是在幫助編譯器。
我們知道,到那時,hV引用了類型為Truck的對象,因此可以將類型為Truck的對象分配給變量T。但是編譯器還不足夠聰明。編譯器希望我們保證,當到達該行并嘗試進行分配時,它將找到一個正在等待它的Truck實例。
添加回答
舉報