泛型是 JDK1.5 以后增加的,它可以帮助我们建立类型安全的集合。
什么是泛型
泛型的本质就是“数据类型的参数化”,处理的数据类型不是固定的,而是可以作为参数传入,可以把“泛型”理解为数据类型的一个占位符(类似:形式参数)
1. 把类型当作是参数一样传递。、
2. <数据类型> 只能是引用类型。
两个好处:
代码可读性更好【不用强制转换】
程序更加安全【只要编译时期没有警告,运行时期就不会出现 ClassCastException 异常】
编码时采用泛型写的类型参数,编译器会在编译时去掉,这称之为“类型擦除”。
泛型主要用于编译阶段,编译后生成的字节码 class 文件不包含泛型中的类型信息,涉及类型转换仍然是普通的强制类型转换。
类型参数在编译后会被替换成Object,运行时虚拟机并不知道泛型
定义泛型
泛型标记 | 对应单词 | 说明 |
E | Element | 在容器中使用,表示容器中的元素 |
T | Type | 表示普通的 JAVA 类 |
K | Key | 表示键,例如:Map 中的键 Key |
V | Value | 表示值 |
N | Number | 表示数值类型 |
? | 表示不确定的 JAVA 类型 |
语法结构
普通java类泛型标记
public class 类名<泛型表示符号> {
}
public class GenericDemo1 {
private static class Generic<T> { // 定义一个泛型类 ,泛型类型为T,【根据实例化的时候定义详细引用类型
private T flag; // T泛型类型待定
public void setFlag(T flag) {
this.flag = flag;
}
public T getFlag() {
return flag;
}
}
public static void main(String[] args) {
Generic<String> generic = new Generic<>(); // 实例化泛型类,泛型为String类型,name这个类的所有 T 都将变成String
generic.setFlag("admin");
String flag = generic.getFlag();
System.out.println(flag);
Generic<Integer> generic1 = new Generic<>();// 实例化泛型类,泛型为String类型,name这个类的所有 T 都将变成integer【int的包装类】
generic1.setFlag(100);
Integer flag1 = generic1.getFlag();
System.out.println(flag1);
}
}
泛型接口
泛型接口 和 泛型类 的声明方式一致。泛型接口的具体类型需要在实现类中进行声明。
public interface 接口名<泛型表示符号> {
}
public class interfaceimp implements 接口名<泛型具体类型>{
@Override
······
}
public class GenericDemo2 {
public interface Igeneric<T> { // 定义一个 接口泛型
T getName(T name);
}
public static class IgenericImpl implements Igeneric<String> { // 实现接口类,并将泛型定义为String类型
@Override
public String getName(String name) { // 实现接口类,重写里面的getName方法
return name;
}
public int getAge(int a){
return a;
}
}
public static void main(String[] args) {
IgenericImpl igeneric = new IgenericImpl(); // 直接实例化接口实现类
String name = igeneric.getName("张三");
System.out.println(name);
Igeneric<String> igeneric1 = new IgenericImpl(); // 接口向上转型为接口Igeneric,调用getName,因为此方法已经被IgennericImp实现,所以可以调用
String name1 = igeneric1.getName("kkkkk");
// int age = igeneric1.getAge(14); // 因为igeneric1 被认为是 Igeneric类型 ,而接口中没有getAge这个方法,所以不能调用
int age1 = igeneric.getAge(16); // getAge是接口实现类里面的方法,所以可以调用
System.out.println(name1);
}
}
泛型方法是指将方法的参数类型定义成泛型,以便在调用时接收不同类型的参数。
类型参数可以有多个,用逗号隔开,如:<E,C,W>。定义时,类型参数一般放到返回值前面。
调用泛型方法时,不需要像泛型类那样告诉编译器是什么类型,编译器可以自动推断出类型来。–》这一点就像解释性语言不用写数据类型一样
泛型方法—非静态方法
public <泛型表示符号> void getName(【形参类型】泛型表示符号 【形参名字】name){
}
public <泛型表示符号> 【返回类型】泛型表示符号 getName(【形参类型】泛型表示符号 【形参名字】name){
}
public class demo3_link {
public <T> void setName(T name){ // 非静态方法--方法泛型
System.out.println(name);
}
public <T> T getName(T name){
return name;
}
}
public class GenericDemo3 {
public static void main(String[] args) {
demo3_link methodGeneric = new demo3_link(); // 实例化非静态方法的类
methodGeneric.setName("whoami"); // 调用非静态方法--泛型 泛型类型编译器自动判断出来 ====>String类型
methodGeneric.setName(123123); // ====>int类型
demo3_link methodGeneric2 = new demo3_link();
String name = methodGeneric2.getName("whereisme");
Integer name1 = methodGeneric2.getName(321321);
System.out.println(name1);
System.out.println(name);
}
}
泛型方法—静态方法
静态方法中使用泛型时有一种情况需要注意一下,那就是静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
public static <泛型表示符号> void setName(泛型表示符号name){
}
public static <泛型表示符号> 泛型表示符号 getName(泛型表示符号name){
}
public class GenericDemo4 {
public static class MethodGeneric {
public static <T> void setName(T name) {
System.out.println(name);
}
public static <T> T getName(T name) {
return name;
}
}
public static void main(String[] args) {
// 类名.静态方法名的格式来调用静态方法 ,也是自动识别泛型具体类型
MethodGeneric.setName("张三");
MethodGeneric.setName(1010122);
// 实例化类,用对象名.方法名调用, 也是自动识别泛型具体类型
MethodGeneric methodGeneric2 = new MethodGeneric();
String name = methodGeneric2.getName("new张三");
Integer name1 = methodGeneric2.getName(123);
System.out.println(name1);
System.out.println(name);
}
}
泛型方法与可变参数
在泛型方法中,泛型也可以定义可变参数类型
-
可变参数的注意事项
-
这里的变量其实是一个数组
-
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
-
public <泛型表示符号> void showMsg(泛型表示符号... agrs){ // ...表示可变
}
public class Metherd5_link {
public <T> void method(T... args) { // 遍历方法 ... 这三个点为可变
for (T t : args) {
System.out.println(t);
}
}
}
public class Metherd5 {
public static void main(String[] args) {
Metherd5_link methodGeneric = new Metherd5_link();
String[] arr = new String[]{"a","b","c"};
Integer[] arr2 = new Integer[]{1,2,3};
methodGeneric.method(arr); // 遍历abc
methodGeneric.method(arr2);// 遍历123
}
}
通配符和上下限定
无界通配符
“?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。解决当具体类型不确定的问题
public void showFlag(Generic<?> generic){
}
public class Unbounded_wildcard {
public static class Generic<T> { // 定义一个一个泛型类
private T flag;
public void setFlag(T flag) {
this.flag = flag;
}
public T getFlag() {
return this.flag;
}
}
public static class ShowMsg {
public void showFlag(Generic<?> generic) { // “?”表示类型通配符,用于代替具体的类型。它只能在“<>”中使用。可以解决当具体类型不确定的问题。
System.out.println(generic.getFlag());
}
}
public static void main(String[] args) {
ShowMsg showMsg = new ShowMsg();
Generic<Integer> generic = new Generic<>();
generic.setFlag(202222); // integer
showMsg.showFlag(generic);
Generic<Number> generic1 = new Generic<>();
generic1.setFlag(50); // Numer
showMsg.showFlag(generic1);
Generic<String> generic2 = new Generic<>();
generic2.setFlag("this is flag3"); // 字符串类型
showMsg.showFlag(generic2);
}
}
通配符的上限限定
上限限定表示通配符的类型是 T 类以及 T 类的子类或者 T 接口以及T 接口的子接口。该方式同样适用于与泛型的上限限定。
public void showFlag(Generic<? extends Number> generic){
}
通配符的下限限定
下限限定表示通配符的类型是 T 类以及 T 类的父类或者 T 接口以及T 接口的父接口。注意:该方法不适用泛型类。
public void showFlag(Generic<? super Integer> generic){
}
泛型总结
泛型主要用于编译阶段,编译后生成的字节码 class 文件不包含泛型中的类型信息。类型参数在编译后会被替换成 Object,运行时虚拟机并不知道泛型
1. 基本类型不能用于泛型。
Test<int> t; 这样写法是错误,我们可以使用对应的包装类;Test<Integer> t ;
2. 不能通过类型参数创建对象。
T elm = new T(); 运行时类型参数 T 会被替换成 Object,无法创建T 类型的对象,容易引起误解,所以在 Java 中不支持这种写法。
原文地址:http://www.cnblogs.com/e-link/p/16815580.html