導(dǎo)語
這里是導(dǎo)語脸甘。對(duì)恳啥,沒錯(cuò),這里就是導(dǎo)語丹诀,沒有前幾次的代碼情書钝的。喜歡情書的關(guān)注我,去看我之前的Java文章吧铆遭。
主要內(nèi)容
- 泛型技術(shù)的產(chǎn)生背景
- 泛型操作的實(shí)現(xiàn)
- 通配符的使用
- 泛型接口
- 泛型方法
具體內(nèi)容
泛型的引出
現(xiàn)在要求定義一個(gè)表示坐標(biāo)的操作類(Point)硝桩,在這個(gè)類里面要求保存有以下幾種坐標(biāo):
- 保存數(shù)字:x = 10、y = 20枚荣。
- 保存小數(shù):x = 10.2碗脊、y = 20.3。
- 保存字符串:x = 東經(jīng)20度橄妆、y = 北緯15度衙伶。
現(xiàn)在這個(gè)Point類設(shè)計(jì)的關(guān)鍵就在于x與y這兩個(gè)變量的類型設(shè)計(jì)上祈坠。必須有一種類型可以保存這三類數(shù)據(jù)。首先想到的一定是Object類型:
- int:int自動(dòng)裝箱為Integer矢劲,Integer向上轉(zhuǎn)型為Object颁虐。
- double:double自動(dòng)裝箱為Double,Double向上轉(zhuǎn)型為Object卧须。
- String:直接向上轉(zhuǎn)型為Object。
范例:初期設(shè)計(jì)如下
public class Point {
private Objects x;
private Objects y;
public Objects getX() {
return x;
}
public void setX(Objects x) {
this.x = x;
}
public Objects getY() {
return y;
}
public void setY(Objects y) {
this.y = y;
}
}
下面重復(fù)的演示三個(gè)程序儒陨,分別使用各個(gè)不同的數(shù)據(jù)類型花嘶。
在Point類里面保存整型數(shù)據(jù)
public class TestDemo {
public static void main(String args[]) {
// 第一步:設(shè)置數(shù)據(jù)
Point p = new Point();
p.setX(10);
p.setY(20);
// 第二步:取出數(shù)據(jù)
int x = (Integer) p.getX();
int y = (Integer) p.getY();
System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "蹦漠。");
}
}
輸出結(jié)果
x坐標(biāo):10椭员,y坐標(biāo):20。
在Point類里面保存小數(shù)
public class TestDemo {
public static void main(String args[]) {
// 第一步:設(shè)置數(shù)據(jù)
Point p = new Point();
p.setX(10.2);
p.setY(20.3);
// 第二步:取出數(shù)據(jù)
double x = (Double) p.getX();
double y = (Double) p.getY();
System.out.println("x坐標(biāo):" + x + "笛园,y坐標(biāo):" + y + "隘击。");
}
}
輸出結(jié)果
x坐標(biāo):10.2,y坐標(biāo):20.3研铆。
在Point類里面保存字符串
public class TestDemo {
public static void main(String args[]) {
// 第一步:設(shè)置數(shù)據(jù)
Point p = new Point();
p.setX("東經(jīng)20度");
p.setY("北緯15度");
// 第二步:取出數(shù)據(jù)
String x = (String) p.getX();
String y = (String) p.getY();
System.out.println("x坐標(biāo):" + x + "埋同,y坐標(biāo):" + y + "。");
}
}
輸出結(jié)果
x坐標(biāo):東經(jīng)20度棵红,y坐標(biāo):北緯15度凶赁。
此時(shí)的代碼已經(jīng)利用了Object數(shù)據(jù)類型解決了一切的開發(fā)問題,可是解決的關(guān)系是靠的是Object逆甜,于是失敗的關(guān)鍵也在于Object虱肄。
范例:觀察錯(cuò)誤的代碼
public class TestDemo {
public static void main(String args[]) {
// 第一步:設(shè)置數(shù)據(jù)
Point p = new Point();
p.setX("東經(jīng)20度");
p.setY(10); // 這里改為數(shù)字
// 第二步:取出數(shù)據(jù)
String x = (String) p.getX();
String y = (String) p.getY();
System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "交煞。");
}
}
代碼不需要執(zhí)行就可以看到程序的問題咏窿,因?yàn)樵诖娣诺臅r(shí)候是int(Integer),而取的時(shí)候使用的是String素征,兩個(gè)沒有發(fā)生關(guān)系的對(duì)象之間要發(fā)生強(qiáng)制轉(zhuǎn)換集嵌,就一定會(huì)產(chǎn)生ClassCastException。
向上轉(zhuǎn)型的核心目的在于統(tǒng)一操作的參數(shù)上稚茅,而向下轉(zhuǎn)型的目的是操作子類的特殊功能纸淮,可是現(xiàn)在的問題發(fā)現(xiàn)向下轉(zhuǎn)換是一件非常不安全的操作,那么這一操作應(yīng)該在代碼運(yùn)行之前就已經(jīng)能夠自動(dòng)的排查出來這是最好的選擇亚享⊙士椋可是之前的技術(shù)做不到。
泛型操作的實(shí)現(xiàn)
從JDK1.5之后開始增加了泛型技術(shù)欺税,而泛型技術(shù)的核心意義在于:類的定義的時(shí)候侈沪,可以使用一個(gè)標(biāo)記揭璃,此標(biāo)記就表示類中屬性或方法參數(shù)的類型標(biāo)記,在使用的時(shí)候才動(dòng)態(tài)的設(shè)置類型亭罪。
范例:修改代碼
// 此時(shí)設(shè)置的T在Point類定義上只表示一個(gè)標(biāo)記瘦馍,在使用的時(shí)候需要為其設(shè)置具體的類型
public class Point<T> { // Type = T,是一個(gè)類型应役,也可是任何標(biāo)記
private T x; // 此屬性的類型不知道情组,由Point類使用時(shí)動(dòng)態(tài)決定
private T y; // 此屬性的類型不知道,由Point類使用時(shí)動(dòng)態(tài)決定
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public T getY() {
return y;
}
public void setY(T y) {
this.y = y;
}
}
在使用Point類的時(shí)候才去設(shè)置標(biāo)記的內(nèi)容箩祥,也就是設(shè)置了類中的屬性的類型院崇。
使用String
public class TestDemo {
public static void main(String args[]) {
// 第一步:設(shè)置數(shù)據(jù)
Point<String> p = new Point<String>();
p.setX("東經(jīng)20度");
p.setY("北緯15度"); // 如果使用p.setY(10);會(huì)報(bào)錯(cuò)
// 第二步:取出數(shù)據(jù)
String x = (String) p.getX();
String y = (String) p.getY();
System.out.println("x坐標(biāo):" + x + ",y坐標(biāo):" + y + "袍祖。");
}
}
使用了泛型之后底瓣,所有類中屬性的類型都是動(dòng)態(tài)設(shè)置的,而所有使用泛型標(biāo)記的方法參數(shù)類型也都發(fā)生改變蕉陋,這樣就相當(dāng)于避免了向下轉(zhuǎn)型的問題捐凭,從而解決了類轉(zhuǎn)換的安全隱患。
但是需要特別說明的是凳鬓,如果想要使用泛型茁肠,那么它能夠采用的類型只能夠是類,即:不能是基本類型村视,只能夠是引用類型(可以使用Integer)官套。
對(duì)于泛型有兩點(diǎn)說明:
- 如果在使用泛型類或者是接口的時(shí)候,沒有設(shè)置泛型的具體類型蚁孔,那么會(huì)出現(xiàn)編譯時(shí)的警告奶赔,同是為了保證程序不出錯(cuò),所有的泛型都將使用Object表示杠氢。
- 從JDK1.7開始可以簡(jiǎn)化聲明泛型站刑。
在JDK1.7之后實(shí)例化的時(shí)候可以寫成:
Point<Integer> p = new Point<>();
通配符
為了更好的理解通配符的作用,下面先來觀察一段程序鼻百。
public class Message<T> {
private T msg;
public T getMsg() {
return msg;
}
public void setMsg(T msg) {
this.msg = msg;
}
}
public class TestDemo {
public static void main(String args[]) {
Message<String> m = new Message<String>();
m.setMsg("Hello World !");
fun(m); // 引用傳遞
}
public static void fun(Message<String> temp) {
System.out.println(temp.getMsg());
}
}
輸出結(jié)果
Hello World !
以上代碼為Message類設(shè)置的是一個(gè)String型的泛型類型绞旅,但是如果換成其它類型就不能夠使用了。
先看一組錯(cuò)誤代碼
public static void fun(Message temp) { // 不設(shè)置接收參數(shù)為泛型温艇,就是Object類型
temp.setMsg("Hello"); // 如果傳過來的泛型不為String就會(huì)報(bào)錯(cuò)
System.out.println(temp.getMsg());
}
正確的代碼如下
public static void fun(Message<?> temp) { // 設(shè)置為通配符問號(hào)因悲,表示不能夠設(shè)置,只能夠取出
System.out.println(temp.getMsg());
}
在“?”通配符基礎(chǔ)上還會(huì)有兩個(gè)子的通配符:
- ? extends類:設(shè)置泛型上限勺爱,可以在聲明上和方法參數(shù)上使用晃琳。
- ? extends Number:意味著可以設(shè)置Number或者Number的子類(Integer、Double...)。
- ? super類:設(shè)置泛型下限卫旱,方法參數(shù)上使用人灼。
- ? super String:意味著只能夠設(shè)置String或者是它的父類Object。
范例:設(shè)置泛型的上限
public class Message<T extends Number> {
private T msg;
public T getMsg() {
return msg;
}
public void setMsg(T msg) {
this.msg = msg;
}
}
public class TestDemo {
public static void main(String args[]) {
Message<Integer> m = new Message<Integer>();
m.setMsg(100);
fun(m); // 引用傳遞
}
public static void fun(Message<? extends Number> temp) {
System.out.println(temp.getMsg());
}
}
如果設(shè)置了非Number或者其子類的話顾翼,那么將出現(xiàn)語法錯(cuò)誤投放。
范例:設(shè)置泛型下限
public class Message<T> {
private T msg;
public T getMsg() {
return msg;
}
public void setMsg(T msg) {
this.msg = msg;
}
}
public class TestDemo {
public static void main(String args[]) {
Message<String> m = new Message<String>();
m.setMsg("Hello");
fun(m); // 引用傳遞
}
public static void fun(Message<? super String> temp) {
System.out.println(temp.getMsg());
}
}
泛型接口
在之前都是將泛型定義在了一個(gè)類里面,那么泛型也可以定義在接口上聲明适贸,稱為泛型接口灸芳。
范例:定義泛型接口
public interface Message<T> {
public void print(T t);
}
定義子類有兩種形式。
形式一:在子類繼續(xù)設(shè)置泛型
// 子類也繼續(xù)使用泛型拜姿,并且父接口使用和子類同樣的泛型標(biāo)記
public class MessageImpl<T> implements Message<T> {
public void print(T t) {
System.out.println(t);
}
}
public class TestDemo {
public static void main(String args[]) {
Message<String> msg = new MessageImpl<String>();
msg.print("Hello World !");
}
}
形式二:在子類不設(shè)置泛型耗绿,而為父接口明確的定義一個(gè)泛型類型
// 子類也繼續(xù)使用泛型,并且父接口使用和子類同樣的泛型標(biāo)記
public class MessageImpl implements Message<String> {
public void print(String t) {
System.out.println(t);
}
}
public class TestDemo {
public static void main(String args[]) {
Message<String> msg = new MessageImpl();
msg.print("Hello World !");
}
}
泛型方法
泛型方法不一定要定義在支持泛型的類里面砾隅。
范例:泛型方法定義
public class TestDemo {
public static void main(String args[]) {
String str = fun("Hello");
Sysout.out.println(str.length);
}
// T的類型由傳入的參數(shù)類型決定
public static <T> T fun(T t) {
return t;
}
}
總結(jié)
- 泛型解決的是向下轉(zhuǎn)型所帶來的安全隱患,其核心的組成就是在聲明類或接口的時(shí)候不設(shè)置參數(shù)或?qū)傩缘念愋汀?/li>
- “?”可以接收任意的泛型類型债蜜,只能夠取出晴埂,但是不能夠修改。