泛型是在jdk1.5之後被引入的,技術革新一定是由於前面的技術的缺陷造成的,因此需要了解泛型出現的原因,才能更好的理解和使用泛型。
一:多態性複習
在之前已經寫了一篇關於java多態性的理解總結,鏈接如下:
JAVA多態性
爲節約篇幅,接下來就接上一篇繼續進行。
class Ball{
public void play(){
System.out.println("在玩Ball");
}
}
class BasketBall extends Ball{
public void rule(){
System.out.println("籃球規則爲:....... ");
}
public void play(){
System.out.println("在玩籃球");
}
}
class FootBall extends Ball{
public void play(){
System.out.println("在玩足球");
}
}
public class DemoTest{
public static void main(String args[]){
Ball ball = new BasketBall();//向上類型轉換
playRule(ball);
}
public static void playRule(Ball ball){
BasketBall bb = (BasketBall)ball; //向下類型轉換(強制轉換)
bb.rule();
}
}
拿了一段代碼,上面定義static靜態方法時參數的類型爲Ball,是父類,實際上傳輸的是BasketBall這個子類對象,實現了參數統一,具體的作用優點已經在上面的鏈接文章中給出了。
二:Object的超然地位
在jdk1.9之後,java引入了自動裝箱和自動拆箱機制,這就給基本數據類型轉換成相應的引用數據類型提供了便利。而Object類是又是所有類的父類,他可以接收所有的引用類型。即可以實現將所有的引用類型實例對象轉換成Object的對象(向上類型轉換)。也就可以實現參數的統一,比上面的還要徹底 !
Object ball = new BasketBall();//向上類型轉換
三:由Object引發的問題
成也蕭何敗也蕭何,由於Object的描述範圍的廣泛,也會造成一些安全隱患
class Ball{
//...
}
class BasketBall extends Ball{
public void rule(){
System.out.println("籃球規則爲:....... ");
}
}
class FootBall extends Ball{
public void rule(){
System.out.println("足球規則爲:....... ");
}
}
public class MulChangeUpTest{
public static void main(String args[]){
Ball ball = new BasketBall();
playRule(ball);
}
public static void playRule(Object obj){
FootBall b = (FootBall)obj;
b.rule();
}
}
運行結果:編譯成功,運行報錯
讓人頭疼的是:這個問題他是編譯不報錯的,而是運行時報錯的,這就會造成極大的安全隱患。
有人說爲什麼不去使用instanceof
去判斷一下,-_- 可千萬不要擡槓,我們這是在提出問題,準備去解決問題。但是instanceof
只是一個防治措施,是爲了防止出現崩潰,並不是解決問題的根本措施,治標不治本。
四:泛型出場
我們在瞭解到了由於Object作爲參數帶來的缺點之後,必須要進行解決。我特別喜歡老師說的一句話,轉型會帶來問題那麼就不去轉型。使用別的途徑去解決問題。
class Ball<T>{
// 球的類型可能使用String,或者Integer類型代表
private T ballType;
public void setBallType(T ballType){
this.ballType = ballType;
}
public T getBallType(){
return this.ballType;
}
}
public class DemoTest{
public static void main(String args[]){
Ball<Integer> ball = new Ball<Integer>();
ball.setBallType(10);
int type = ball.getBallType();
System.out.println(b.getBallType());
}
}
當前類型被死死的限制爲了Integer,就不會造成這種安全隱患了。
此時編譯和運行都正確。使用泛型<T>
來表示Type類型,這個Ball類內部的所有T代表的類型,是在運行時動態決定的。
如果不按照泛型定義的類型進行賦值:
public class DemoTest{
public static void main(String args[]){
Ball<Integer> ball = new Ball<Integer>();
ball.setBallType("籃球");
}
}
報錯信息:
如此,使用了泛型後在編譯期間就會報錯,就將這種安全隱患解決了。
五:參數統一下的泛型:泛型通配符
先看一段代碼
class Ball<T>{
// 球的類型可能使用String,或者Integer類型代表
private T ballType;
public void setBallType(T ballType){
this.ballType = ballType;
}
public T getBallType(){
return this.ballType;
}
}
public class DemoTest{
public static void main(String args[]){
Ball<Integer> ball = new Ball<Integer>();
ball.setBallType(10);
playRule(ball);
}
public static void playRule(Ball<String> b){
System.out.println(b.getBallType());
}
}
先想一想,我們使用Object的最大優點是什麼? 就是實現了參數類型的統一。但是使用了上面的泛型來定義playRule的參數類型時,將傳入的參數類型限制在了String類型。Integer類型的就會報錯,這與我們改善Object的初衷是相悖的。
此時就需要一個泛型用於描述一切的類型。即泛型通配符
<?>
public static void playRule(Ball<?> b){
System.out.println(b.getBallType());
}
六:總結
技術改進的道理如果明白,那麼在使用時自然就更會印象深刻,接受更快。在理解他的設計思想和改進原因下,更會事半功倍。