前言
今天群里有位同學(xué)提了一個(gè)關(guān)于
Object[]數(shù)組向下強(qiáng)制類型轉(zhuǎn)換的問題,個(gè)人覺得這個(gè)問題還是有必要思考總結(jié)一下,因?yàn)楹芏嗳说谝谎鄱紩?huì)覺得:奇怪,這里怎么會(huì)拋出ClassCastException呢?
正文
首先我們敲一遍這位同學(xué)提的問題
1. 編碼
Coffee.java
模擬數(shù)據(jù)庫查詢實(shí)體咖啡類
public class Coffee {
private String name;
private Double price;
public Coffee() {
}
public Coffee(String name, Double price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getPrice() {
return price;
}
public void setPrice(Double price) {
this.price = price;
}
}
模擬業(yè)務(wù)代碼
TestCoffee.java
public class TestCoffee {
private static List<Coffee> queryFromDatabase() {
List<Coffee> result = new ArrayList<>();
Coffee latte = new Coffee("latte", 38.0);
Coffee latte2 = new Coffee("latte", 28.0);
Coffee mocha = new Coffee("mocha", 58.0);
result.add(latte);
result.add(latte2);
result.add(mocha);
return result;
}
public static void main(String[] args) {
// 1. 模擬從數(shù)據(jù)庫查詢出所有咖啡
List<Coffee> coffees = queryFromDatabase();
// 2. 取出所有l(wèi)atte拿鐵類咖啡并放入一個(gè)Coffee[]數(shù)組中
Coffee[] lattes = (Coffee[]) coffees.stream()
.filter(coffee -> coffee.getName().equals("latte"))
.toArray();
// 3. 這里只是測試打印,實(shí)際業(yè)務(wù)代碼可能需要用Coffee[]類型的參數(shù)再去做其他業(yè)務(wù)處理...
System.out.println("latte coffee number is: " + lattes.length);
}
}
測試代碼邏輯:
我們首先模擬從數(shù)據(jù)庫中查詢出所有咖啡數(shù)據(jù),利用Java8提供的Lambda表達(dá)式過濾出所有拿鐵類的咖啡,并最終轉(zhuǎn)為Coffee[] 數(shù)組類型
2. 測試
main方法執(zhí)行拋出如下錯(cuò)誤提示
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Lcom.wilson.algorithms.test.Coffee;
at com.wilson.algorithms.test.TestCoffee.main(TestCoffee.java:30)
這里引出本文重點(diǎn):運(yùn)行時(shí)異常之類型轉(zhuǎn)換異常
ClassCastException
jdk1.0,開始提供該異常,此異常發(fā)生在程序代碼試圖將
一個(gè)對(duì)象轉(zhuǎn)換為一個(gè)并非是自己或自己的子類對(duì)象的時(shí)候
舉個(gè)源碼中拋此異常的例子
// Integer類型
Object v = new Integer(0);
// 強(qiáng)制轉(zhuǎn)換為String類型
System.out.println((String) v);
因此我們總結(jié)一下,運(yùn)行時(shí)異常ClassCastException出現(xiàn)試圖將一個(gè)對(duì)象強(qiáng)制轉(zhuǎn)換為一個(gè)并非是自己或自己的子類對(duì)象的時(shí)候
至此回到我們文章開頭的問題,為什么我將Object[](實(shí)際里面的元素就是Coffee類型),強(qiáng)制轉(zhuǎn)為Coffee[]就會(huì)拋錯(cuò)呢?
拋錯(cuò)分析
實(shí)際上,在java中,Object是所有class類型的根,數(shù)組當(dāng)然也是一種class類型,因此Object[]類型和Coffee[]類型都是Object類型的子類型
但是對(duì)于Object[]類型和Coffee[]類型,二者同為數(shù)組類型,可他們之間并沒有什么父子關(guān)系,而是平級(jí)的關(guān)系
正是因?yàn)楹芏嗤瑢W(xué)誤以為Object[]應(yīng)該是Coffee[]的父類型,才會(huì)隨手寫出導(dǎo)致程序運(yùn)行時(shí)ClassCastException異常二者區(qū)別
Object[]類型與Coffee[]類型的區(qū)別在于其數(shù)組中可以存放任意Object類型的對(duì)象,而Coffee[]則只能存放所有的Coffee類型(包括其子類)對(duì)象-
圖示
關(guān)系圖示.png
總結(jié)
因此,對(duì)于日常開發(fā)中,我們應(yīng)該重視對(duì)Object[] 類型的返回值的處理,不能想當(dāng)然的認(rèn)為他就是Xxx[],隨手進(jìn)行強(qiáng)制類型轉(zhuǎn)換,而導(dǎo)致出現(xiàn)運(yùn)行時(shí)類型轉(zhuǎn)換異常
對(duì)于Lambda表達(dá)式的及早求值方法toArray(),Stream提供了一個(gè)重載的方法來規(guī)避強(qiáng)制類型轉(zhuǎn)換異常的發(fā)生
<A> A[] toArray(IntFunction<A[]> generator);
我們改造一下文章開頭的代碼
Coffee[] lattes = coffees.stream()
.filter(coffee -> coffee.getName().equals("latte"))
.toArray(Coffee[]::new);
通過一個(gè)方法引用作為參數(shù),告知toArray方法,我想要返回的數(shù)組是一個(gè)Coffee[]類型
