一、普通的泛型
比如我们经常使用的List data = new ArrayList;
这里我们直接指定List中存储的是String类型的数据,避免在get的时候还要强制转换。
1.来定义一个简单的泛型:
public class MessageQueue<T> {
private T mMsg = null;
public T getMsg() {
return mMsg;
}
public void addMsg(T msg) {
this.mMsg = msg;
}
public void printMsg() {
System.out.println(this.mMsg);
}
}
2.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = MessageQueue | 基本泛型 = = = = = = = = = ");
MessageQueue<String> mq= new MessageQueue<String>();
mq.addMsg("MQ");
System.out.println(mq.getMsg());
mq.printMsg();
MessageQueue<Integer> mqInt= new MessageQueue<Integer>();
mqInt.addMsg(1);
System.out.println(mqInt.getMsg());
mqInt.printMsg();
}
}
3.可以看到运行的结果是:
= = = = = = = = MessageQueue | 基本泛型 = = = = = = = = =
MQ
MQ
1
1
二、两个或者多个泛型参数
1.还是来定义含有两个参数的泛型类
public class UserInfo<K,V> {
private K key;
private V value;
public void put(K key,V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public V getValue() {
return value;
}
public String toString() {
return this.key.toString() + " | " + value.toString();
}
}
2.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = UserInfo | 两个泛型参数 = = = = = = = = = ");
UserInfo<Integer,String> userInfo = new UserInfo<Integer,String>();
userInfo.put(2,"wxp");
System.out.println(userInfo.toString());
}
}
3.看一下运行的结果:
= = = = = = = = UserInfo | 两个泛型参数 = = = = = = = = =
2 | wxp
第一个参数就是key,第二个参数就是value
三、泛型通配符
1.定义一个简单的泛型,或者直接使用上面的MessageQueue类,都是可以的
public class MsgModel<T> {
private T mMsg = null;
public T getMsg() {
return mMsg;
}
public void addMsg(T msg) {
this.mMsg = msg;
}
@Override
public String toString() {
return this.mMsg.toString();
}
}
2.接着定义一个printStringMsgModel方法,这个方法的参数时一个MsgModel类型,但是它的泛型参数只能只能接收String或Object(String的父类)类型的泛型,这个是受限通配符,用<? extends String>表示。
//受限通配符,限制泛型参数只能接收String或Object类型的泛型
public static void printStringMsgModel(MsgModel<? extends String> model) {
System.out.println("model = "+model.toString());
}
3.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = MsgModel | 通配符 = = = = = = = = = ");
MsgModel<String> msgModel = new MsgModel<String>();
msgModel.addMsg("www");
printStringMsgModel(msgModel);
}
}
4.运行的结果是:
= = = = = = = = MsgModel | 通配符 = = = = = = = = =
model = www
- <? extends UserInfo> 表示上边界通配符,参数类型只能是UserInfo或者其子类
- <? super UserInfo> 表示下边界通配符,参数类型只能是UserInfo或者其父类
5.或者也可以这样定义一个printUserInfoMsgModel方法,表示只能接受UserInfo或者其子类的泛型参数
//受限通配符,限制泛型参数只能接收UserInfo或子类类型的泛型
public static void printUserInfoMsgModel(MsgModel<? extends UserInfo> model) {
System.out.println("model = "+model.getMsg().toString());
}
测试一下:
public class TypeTest {
public static void main(String[] args) {
UserInfo<Integer,String> userInfo1 = new UserInfo<Integer,String>();
userInfo1.put(3,"wxp");
MsgModel<UserInfo> msgModelUserInfo = new MsgModel<UserInfo>();
msgModelUserInfo.addMsg(userInfo1);
printUserInfoMsgModel(msgModelUserInfo);
}
}
运行的结果:
model = 3 | wxp
6.如果是非受限通配符的话直接使用MsgModel<? >这样就可以了
四.泛型接口
1.定义一个泛型接口
public interface IUploadMsg<T> {
public void upload(T msg);
}
2.定义一个UploadAction类,并实现IUploadMsg接口
//泛型接口
public static class UploadAction<T> implements IUploadMsg<T> {
private T mMsg;
public void addMsg(T msg) {
this.mMsg = msg;
upload(msg);
}
@Override
public void upload(T msg) {
System.out.println("uploading msg..."+msg.toString());
}
}
3.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = IUploadMsg | 泛型接口 = = = = = = = = = ");
UploadAction<String> uploadAction = new UploadAction<String>();
uploadAction.addMsg("xxx");
UploadIntAction uploadIntAction = new UploadIntAction();
uploadIntAction.addMsg(4);
}
}
4.看一下运行结果:
= = = = = = = = IUploadMsg | 泛型接口 = = = = = = = = =
uploading msg...xxx
uploading msg...4
五、泛型方法
1.定义一个DownloadMsg类,它有一个download方法,可以传入一个泛型参数msg
public class DownloadMsg {
public static <T> void download(T msg) {
System.out.println("Downloading msg..."+msg.toString());
}
}
2.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = DownloadMsg | 泛型方法 = = = = = = = = = ");
DownloadMsg.download("ppp");
DownloadMsg.download(5.0);
}
}
3.看一下运行结果:
= = = = = = = = DownloadMsg | 泛型方法 = = = = = = = = =
Downloading msg...ppp
Downloading msg...5.0
六、泛型方法返回泛型实例
1.定义一个普通的泛型类
public class GenerateInfo<T> {
private T mInfo;
public void setInfo(T info) {
mInfo = info;
}
public T getInfo() {
return mInfo;
}
public String toString() {
System.out.println(this.mInfo.toString());
return mInfo.toString();
}
}
2.定义一个方法,传入泛型参数,返回一个泛型实例
public static <T> GenerateInfo<T> genInfo(T info) {
GenerateInfo<T> temp = new GenerateInfo<T>();
temp.setInfo(info);
return temp;
}
3.测试代码:
public class TypeTest {
public static void main(String[] args) {
System.out.println("= = = = = = = = GenerateInfo | 泛型方法返回泛型实例 = = = = = = = = = ");
GenerateInfo<String> info = genInfo("wwwxxxppp");
info.toString();
UserInfo<String,Integer> userInfo2 = genUserInfo("wxp",5);
System.out.println(userInfo2.toString());
}
}
4.运行结果:
= = = = = = = = GenerateInfo | 泛型方法返回泛型实例 = = = = = = = = =
wwwxxxppp
wxp | 5
七、类型擦除
泛型是 Java 1.5 版本才引进的概念,在这之前是没有泛型的概念的,但显然,泛型代码能够很好地和之前版本的代码很好地兼容。
这是因为,泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除
。
通俗地讲,泛型类和普通类在 java 虚拟机内是没有什么特别的地方。例如:
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
System.out.println(l1.getClass() == l2.getClass());
这个代码输出的就是true,因为编译之后,List中的泛型参数类型全部被设置为Object。
但是如果使用 extends
` ,对类型的上限和下限进行设置,那么类型擦除之后就不是Object了,例如:
public class Erasure <T extends String>{
T object;
public Erasure(T object) {
this.object = object;
}
}
Erasure<String> erasure = new Erasure<String>("hello");
Class eclz = erasure.getClass();
Field[] fs = eclz.getDeclaredFields();
for ( Field f:fs) {
System.out.println("Field name "+f.getName()+" type:"+f.getType().getName());
}
输出:Field name object type:java.lang.String
所以,总结一下,在泛型类被类型擦除的时候,之前泛型类中的类型参数部分如果没有指定上限,如 则会被转译成普通的 Object 类型,如果指定了上限如 则类型参数就被替换成类型上限。
THE END.