Java7以前版本開發升至Java8之注意事項 - 泛型造成ClassCastException的RunTimeException
I. 發生問題的程式結構
下面是發生問題的程式,寫成簡短的例子
import org.junit.Test;
public class Demo {
@Test
public void callMethod() {
System.out.print(String.valueOf(getElements()));
}
static <E> E getElements() {
return (E) "element";
}
}
該段程式無論在Java7或是在Java8皆可進行build,但是在執行該段方法時,當是在Java7的環境會正常執行,反觀Java8卻拋出ClassCastException,如下圖
此問題為何會產生?
II. 發生問題的原因
1. 回傳值確認
先來看在String.valueOf方法中,static generic method 在Java7與Java8所被認定的回傳值型態
Java7
在Java7中該泛型回傳型態認定是Object,再看看Java8
Java8
在Java8中,回傳值型態被認定為char array
2. 方法確認
String類別,valueOf方法所能接受的傳入值如下圖
Java7
Java8
無論是Java7還是Java8皆有支援接取的輸入值型態,再來檢查static的return值,利用下面程式檢查generic的回傳值type
import org.junit.Test;
public class Demo_GetClass {
@Test
public void getType() {
System.out.print(getElements().getClass() );
}
<E> E getElements() {
return (E) "element";
}
}
Java7
回傳的是 class java.lang.String
Java8
同樣也是回傳 class java.lang.String
在Java8竟然是回傳String型態而非char array型態
確認問題
當String要轉為char array時無法強制轉型,必須呼叫toCharArray()方法才可轉成char array
III. 問題的處理方式
在上面的例子Java8中String的方法自動認定其傳入值型態為char array,卻又因為實際回傳的型態是String而造成ClassCastException,那就讓其方法的傳入值改成Object型態如下面程式
import org.junit.Test;
public class Demo_Fix {
@Test
public void callMethod() {
System.out.println(String.valueOf((Object)getElements()) );
checkType();
}
<E> E getElements() {
return (E) "element" ;
}
public void checkType() {
System.out.println("Return type is " + getElements().getClass().getName());
}
}
執行結果如下
確認執行無誤
IV. 結語
於Java8以後,當在呼叫generic時,其回傳值的型態會被直接認定而非以前的Object型態,因此在開發時,應盡量避免於generic方法內直接回傳一個特定值,若為特定值則應直接回傳該型態,避免產生ClassCastException