java参数传入的是一个类名_Java编程细节——泛型的定义(类、接口、对象)、使用、继承...
1. 設(shè)計(jì)泛型的初衷:
1) 主要是為了解決Java容器無法記憶元素類型的問題:
i. 由于Java設(shè)計(jì)之初并不知道會往容器中存放什么類型的元素,因此元素類型都設(shè)定為Object,這樣就什么東西都能放了!
ii. 但是這樣設(shè)計(jì)有明顯的缺點(diǎn):
a. 取出元素的時(shí)候必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換(盡管集合在運(yùn)行時(shí)里面元素的“運(yùn)行時(shí)類型”不變,即元素的getClass返回的還是最初自己的類型而不是Object);
b. 如果不小心往集合里加了不相同類型的元素可能會導(dǎo)致類型異常(進(jìn)行equals、compare比較的時(shí)候尤為明顯);
c. 由于沒有類型就需要在很多地方進(jìn)行強(qiáng)制類型轉(zhuǎn)換,但是這樣做增加了編程的復(fù)雜度,并且代碼也不美觀(臃腫),維護(hù)起來也更加困難;
2) 泛型的概念定義:
i. 從Java 5開始,引入了參數(shù)化類型(Parameterized Type)的概念,改造了所有的Java集合,使之都實(shí)現(xiàn)泛型,允許程序在創(chuàng)建集合時(shí)就可以指定集合元素的類型,比如List就表名這是一個(gè)只能存放String類型的List;
ii. 泛型(Generic):就是指參數(shù)化類型,上面的List就是參數(shù)化類型,因此就是泛型,而String就是該List泛型的類型參數(shù);
3) 泛型的好處:
i. 使集合可以記住元素類型,即取出元素的時(shí)候無需進(jìn)行強(qiáng)制類型轉(zhuǎn)化了,可以直接用原類型的引用接收;
ii. 一旦指定了性參數(shù)那么集合中元素的類型就確定了,不能添加其他類型的元素,否則會直接編譯保存,這就可以避免了“不小心放入其他類型元素”的可能;
iii. 上述保證了如果在編譯時(shí)沒有發(fā)出警告,則在運(yùn)行時(shí)就一定不會產(chǎn)生類型轉(zhuǎn)化異常(ClassCastException);
iv. 顯然,泛型使編程更加通用,并且代碼也更加簡潔,代碼更加容易維護(hù);
2. 創(chuàng)建泛型對象——自動(dòng)類型推斷的菱形語法:
1) 首先,定義泛型引用一定要使用尖括號指定類型參數(shù),例如:List list、Map等,其中的String、Integer之類的就是類型參數(shù);
2) 其次,使用構(gòu)造器構(gòu)造泛型對象的時(shí)候可以指定類型參數(shù)也可以不指定,例如:
i. List list = new List(); // 這當(dāng)然是對的
ii. List list = new List<>(); // 這樣對,因?yàn)長ist的類型參數(shù)可以從引用推斷出!!!但是引用的類型參數(shù)是一定要加的,否則無法推斷;
3) 由于<>很像菱形,因此上面的語法也叫做菱形語法;
4) 錯(cuò)誤提示:引用無類型參數(shù)但構(gòu)造器有類型參數(shù)的寫法是不對的!例如,List list = new List();!!至于為什么不對,這會在泛型原理的章節(jié)中詳細(xì)介紹,這里先記住這樣寫不對就行了!!反正就是一個(gè)原則,泛型引用是一定要指定類型參數(shù)的!!
5) 示例:
3. 定義泛型類、接口:
1) 不僅Java的集合都定義成了泛型,用戶自己也可以定義任意泛型的類、接口,只要在定義它們時(shí)用<>來指定類型參數(shù)即可;
2) 例如:public class Fruit { ... },其中指定了該泛型的類型參數(shù),這個(gè)T是一個(gè)類型參數(shù)名,用戶可以任意命名(就像方法參數(shù)的形參名一樣),只有在定義該泛型的對象時(shí)將T替換成指定的具體類型從而產(chǎn)生一個(gè)實(shí)例化的泛型對象,例如:Fruit fruit = new Fruit<>(...);
3) 類型形參可以在整個(gè)接口、類體內(nèi)當(dāng)成普通類型使用,集合所有可使用普通類型的地方都可以使用類型形參,例如:
可以看到,在接口內(nèi)/類體內(nèi)甚至還可以使用該類型形參運(yùn)用泛型!例如上面makeSet方法返回一個(gè)泛型Set;
4) 定義泛型構(gòu)造器:泛型的構(gòu)造器還是類名本身,不用使用菱形語法,例如:
定義構(gòu)造器無需MyGeneric(...) { ... }了,只有在new的時(shí)候需要用到菱形語法;
4. 實(shí)現(xiàn)/繼承泛型接口/泛型類:
1) 定義泛型和使用泛型的概念:主要區(qū)別就是定義和使用
i. 那Java的方法做類比,Java的方法在定義的時(shí)候使用的都是形參(虛擬參數(shù)),但是在調(diào)用方法(使用方法)的時(shí)候必須傳入實(shí)參;
ii. 同樣泛型也有這個(gè)特點(diǎn),泛型的類型參數(shù)和方法的參數(shù)一樣,也是一種參數(shù),只不過是一種特殊的參數(shù),用來表示未知的類型罷了;
iii. 因此,泛型也是在定義的時(shí)候必須使用形參(虛擬參數(shù),用戶自己隨意命名),但是在使用泛型的時(shí)候(比如定義泛型引用、繼承泛型)就必須使用實(shí)參,而泛型的實(shí)參就是具體的類型,像String、Integer等具體的類型(當(dāng)然也可以是自定義類型);
2) 泛型定義的時(shí)候使用形參,例如:public class MyGeneric { ... } // T就是一個(gè)自己隨意命名的類型形參
3) 使用泛型的時(shí)候必須傳入實(shí)參:
i. 定義引用(對象)的時(shí)候毫無疑問,肯定需要傳實(shí)參:ArrayList list = ...; // 必須用具體的類型,像這里就是String來代替形參,即實(shí)參
ii. 實(shí)現(xiàn)/繼承一個(gè)泛型接口/類的時(shí)候:
你在實(shí)現(xiàn)/繼承一個(gè)接口/類的時(shí)候?qū)嶋H上是在使用該接口/類,比如:public class Son extends Father { ... }中Father這個(gè)類就是正在被使用,毫無疑問,必定是在使用;
因此泛型其實(shí)無法繼承/實(shí)現(xiàn),因?yàn)樵趯?shí)現(xiàn)/繼承的時(shí)候必須為泛型傳入類型實(shí)參,給定實(shí)參后它就是一個(gè)具體的類型了,就不再是泛型了
示例:public class MyType extends MyGeneric { ... } // implements、extends的時(shí)候必須傳入類型實(shí)參,因?yàn)閷?shí)在使用泛型。原則上,任何編程語言都不允許泛型模板層層繼承!!
4) 繼承之后,父類/接口中的所有方法中的類型參數(shù)都將變成具體的類型,你在子類中覆蓋這些方法的時(shí)候一定要用具體的類型,不能繼續(xù)使用泛型的類型形參了,例如:
這一定能保證,這三個(gè)方法都是從父類中繼承來的,只不過類型形參T被實(shí)例化成了String;
5. 泛型參數(shù)繼承:
1) 上面派生出來的類不是泛型,是一個(gè)實(shí)體類型,因?yàn)槠淅^承的泛型是具有類型實(shí)參的,而Java還支持一種特殊的語法,可以讓你從泛型繼續(xù)派生出泛型,而泛型的類型參數(shù)可以繼續(xù)傳承下去;
2) 語法如下:
即子泛型可以傳承父泛型的泛型參數(shù),那么在子類中泛型參數(shù)T就和父類的完全相同,還是照常使用(和父類一樣正常使用);
3) 注意:
i. 這里extends Father了,因此父類泛型Father就是被使用了,而按照之前講的規(guī)則,使用給一個(gè)泛型是必須要指定類型實(shí)參的!因此這里的這個(gè)語法是一種特殊語法,Java專門為這種語法開了后門,這種語法只有在類型參數(shù)傳承的時(shí)候才會用到(即上面這種應(yīng)用);
ii. 一旦使用了這種語法,就表示要進(jìn)行類型參數(shù)的傳承了(即父類的T傳遞給子類繼續(xù)使用,因此子類也是一個(gè)跟父類一樣的泛型);
iii. 并且一旦使用了這種語法,那么子類定義中的Son和extends Father中的類型參數(shù)必須和定義父類時(shí)的類型參數(shù)名完全一樣!!
a. 以下三種情況全部錯(cuò)誤(全部發(fā)生編譯報(bào)錯(cuò)):
必須全部使用和父類定義相同的類型參數(shù)名(T)!才行,這是Java語法的特殊規(guī)定;
4) 其實(shí)Java容器中很多類/接口都是通過類型參數(shù)傳承來定義的:
i. 最典型的例子就是:public interface List extends Collection { ... }
ii. 雖然"如果A是B的父類,但Generic不是Generic"的父類,但"如果A是B的父類,那A一定是B的父類"!這是一定的;
iii. 因?yàn)轭愋蛥?shù)傳承的定義方式本身就是:Son extends Father,那Father一定是Son的父類咯!
6. 在使用泛型的時(shí)候可以不使用菱形語法指定實(shí)參,直接裸用類型名:
1) 例如:
i. 定義引用(對象)時(shí)裸用類名:ArrayList list = new ArrayList(); // 當(dāng)然也可以寫成ArrayList list = new ArrayList<>();
ii. 實(shí)現(xiàn)/繼承:public class MyType extends MyGeneric { ... }
上面使用的類型或者接口在定義的時(shí)候都是泛型!!但是使用它們的時(shí)候忽略類型參數(shù)(都不用加菱形);
2) Java規(guī)定,一個(gè)泛型無論如何、在任何地方、不管如何使用,它永遠(yuǎn)都是泛型,因此這里既是你忽略類型實(shí)參它底層也是一個(gè)泛型,那么它的類型實(shí)參會是什么呢?既然我們沒有顯式指定,那么Java肯定會隱式為其指定一個(gè)類型實(shí)參吧?
3) 答案是肯定的,如果使用泛型的時(shí)候不指定類型實(shí)參,那么Java就會用該泛型的“原生類型“來作為類型實(shí)參傳入,那么“原生類型“是什么呢?這里先不介紹,會在下一章的”泛型原理“里詳細(xì)分解;但是我們這里可以先透露一下,Java集合的原生類型基本都是Object,因此像上面的ArrayList list = new ArrayList();寫法其實(shí)傳入的是Object類型實(shí)參,即ArrayList!
總結(jié)
以上是生活随笔為你收集整理的java参数传入的是一个类名_Java编程细节——泛型的定义(类、接口、对象)、使用、继承...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql 怎么实现随机查询并分页,不重
- 下一篇: 根据excel生成mysql数据库,根据