javaSE泛型——萬用字元、泛型介面、型別擦出
一、萬用字元
1.萬用字元的概念
前面我們學習的泛型都已經解決了很多問題了,但是我們在使用的時候還是指定了型別的。比如泛型類,我們在建立這個類的物件的時候就指定了是什麼型別,然後就只能建立這個型別的物件,那麼我們有時候想要建立任意型別的物件的時候我們可以使用萬用字元。
1.1無解萬用字元"?"
class Message<T> { private T messgae; public T getMessage() { return messgae; } public void setMessage(T messgae) { this.messgae = messgae; } } public class TestTongpeifu { //1.萬用字元的使用 public static void main(String[] args) { Message<Integer> message=new Message();//在這可以接收整型的資料 message.setMessage(55); Message<String> message1=new Message<>();//也可以接收字串 message1.setMessage("你好"); Message<Double> message2=new Message<>();//也可以接收雙精度的小數 message2.setMessage(12.35); fun(message); fun(message1); fun(message2); } public static void fun(Message<?> temp){//這個地方就使用了萬用字元 //但是由於萬用字元不是具體的型別,所以我們不能修改值-> temp.setMessage(100);//error System.out.println(temp.getMessage()); //當我們使用了萬用字元以後就可以接收任意型別的值了,可以體統給使用者的方法是它們可以任意的修改 } }
從上面的例子我們可以看出來,使用萬用字元以後,我們的同一個方法可以接收不同型別的資料,這樣的話我們的泛型顯得就更加的有用了。但是一定需要注意的是:在使用萬用字元的時候我們的資料是不可以更改的,因為在萬用字元中不知道具體的型別是什麼,所以不可以修改。
2.受限泛型
我們由萬用字元又有了兩個子萬用字元:
extends類:設定泛型上限
super類:設定泛型下限
2.1泛型上限
(1)設定型別萬用字元的上限
class Generics<T extends Number>{ private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } public void show(Generics<?> T){ System.out.println("show():"+getObj()); } } public class TestGenerics { public static void main(String[] args) { Generics<Integer> generics1=new Generics<>();//建立Generics的例項化物件 generics1.setObj(20); Generics<Double> generics2=new Generics<>();//例項化物件 generics2.setObj(12.05); generics1.show(generics1); info(generics1); generics2.show(generics2); info(generics2); } public static void info(Generics<? extends Number> t){//這個萬用字元就是上限的萬用字元,它所能接收的型別只能是Number的子類 //這個方法的作用是:接收Generics的任意物件並將其內容列印 System.out.println("info():"+t.getObj()); } }
2.2泛型下限
//泛型下限 class Message<T>{ private T message; public T getMessage() { return message; } public void setMessage(T message) { this.message = message; } } public class TestGenerics{ public static void main(String[] args) { Message<String> message=new Message<>(); message.setMessage("nihao"); fun(message); } public static void fun(Message<? super String> temp){ //泛型下限是可以修改的 temp.setMessage("bit");//修改了我們開始的值 System.out.println(temp.getMessage()); } }
通過上賣弄我們可以看到,泛型下限是可以修改的,我們本來設定的是"nihao",但是通過泛型下限的方法將這個值修改了;此外還需要注意,經過泛型下限修飾的方法裡面的型別只能是這個型別以及這個型別的父類。
二、泛型介面
泛型出了可以定義在類中,也可以定義在介面中。我們稱這種該介面為泛型介面。
1.定義泛型介面的格式:
interface IMessage{
public void print(T t);
}
下面是對泛型介面的實現,一共有三種方法:
(1)匿名內部類實現
(2)在子類定義時繼續使用泛型
(3)在子類實現介面的時候明確給出具體型別
具體實現如下:
public interface IMessage<T>{
void print(T t);
}
public class TestInterface {
public static void main(String[] args) {
// (1)匿名內部類實現:在建立介面的物件的時候我們將指定具體的型別
IMessage<String> iMessage =new IMessage<String>() {
@Override
public void print(String s) {
System.out.println(s);
}
};
}
}
// (2)在子類定義時繼續使用泛型
class IMessage1<T> implements IMessage{
@Override
public void print(T t) {
System.out.println(t);
}
}
// (3)在子類實現介面的時候明確給出具體型別
class IMessage2 implements IMessage<String>{
@Override
public void print(String s) {
System.out.println(s);
}
}
三、型別擦出
泛型只存在於程式碼編譯階段,在進入JVM之前,與泛型相關的資訊都會被擦出掉,即是型別擦出。也就是說,泛型類和普通類對於JVm而言是一樣的。
class Myclass<T>{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
public void testMethod(T t){
System.out.println(t);
}
}
public class Cachu {
public static void main(String[] args) {
Myclass<String> m1=new Myclass<>();
Myclass<Integer> m2=new Myclass<>();
System.out.println(m1.getClass()==m1.getClass());
}
}
上面的例子我們看到的是最後列印的結果是true,說明我們MyClass和MyClass在JVLM中的Class都是MyClass.class。
1.驗證泛型型別在執行時被擦出了的兩種方法
(1)instanceof
(2)getClass
具體如下:
class Myclass<T>{
private T message;
public T getMessage() {
return message;
}
public void setMessage(T message) {
this.message = message;
}
public void testMethod(T t){
System.out.println(t);
}
}
public class Cachu {
public static void main(String[] args) {
Myclass<String> m1=new Myclass<>();
Myclass<Integer> m2=new Myclass<>();
//泛型型別擦出檢驗方法
// //1. instanceof
// System.out.println(message1 instanceof Message);//true
// System.out.println(message2 instanceof Message);//true
//
// //2. getClass
// System.out.println(message1.getClass().getName());//message1物件例項化的型別
// System.out.println(message2.getClass().getName());
}
}
2.對於泛型型別經過擦出以後在執行時候的型別
//class Myclass<T>{
// private T message;
//
// public T getMessage() {
// return message;
// }
//
// public void setMessage(T message) {
// this.message = message;
// }
// public void testMethod(T t){
// System.out.println(t);
// }
//}
//public class Cachu {
// public static void main(String[] args) {
// Myclass<String> m1=new Myclass<>();
// Myclass<Integer> m2=new Myclass<>();
// System.out.println(m1.getClass()==m1.getClass());
// }
//}
結論:
(1)上面這種泛型的型別如果在最後沒有指定具體的型別,那麼在最後經過擦出以後,在執行的時候我們的型別就是Object型別。
(2)但是如果我們有上限的萬用字元,那麼就算沒有指定型別最後的型別就是上限。
四、泛型的總結
-
泛型類
1.1 className<T,S> 使用時指定具體的型別
1.2 ? extends classType 指定型別引數的上限
1.3 ? super classType 指定型別引數下限 -
泛型方法
2.1 returnValue method(T args)
2.2 泛型方法和泛型類是相互獨立
2.3 ? , ? extends classType(不能改內容) , ? super classType(可以改內容) -
泛型介面
3.1 interfaceName
3.2 className implements interfaceName
3.2 className implements interfaceName
3.3 結合匿名內部類使用 -
泛型擦除
4.1 泛型資訊存在於編譯階段,執行時型別擦除
4.2 未指定型別引數上限時,型別統一設定Object
4.3 指定型別引數上限時,型別統一設定為上限型別