前言
才疏学浅,能力有限,文章中有什么错误,欢迎大家评论指出,一起进步~
一、泛型概念
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。 ——百度百科
实际上可以理解为:将对象的类型作为参数,指定到其他类或者方法上,从而保证类型转换的安全性和稳定性。
二、泛型的优势
- 使用泛型,能使代码看起来灵活,容易管理,不容易产生错误.。
- 使用泛型能使代码量减少,产生很多公共代码。
- 使用泛型在代码编译的时候能进行泛型的检查并自动转换,使代码的运行效率得到提高。
- 使用泛型的时候,参数只能是类的类型,不能是简单类型。
- 使用泛型的时候,参数可以有多个。
- 使用泛型的时候,参数也能继承别的类型。
三、泛型的特点
在编译时有效,在编译之后程序会采取去泛型化的措施。在编译过程中,正确检验泛型结果后,会将泛型的相关信息擦出,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。
四、泛型的使用
1、泛型类
定义: 使用泛型定义的类,称为泛型类。
格式: 正常定义类的时候,在类名后面加上<泛型变量>。
例如:
package yg.day14Buffer;
public class GenericityDemo<T> {
}
使用: 在实例化对象的时候指明泛型的具体类型即可。
例如:
public class DemoMain {
public static void main(String[] args) {
GenericityDemo<String> gd = new GenericityDemo<>();
}
}
注意:
1. 泛型类中的成员变量类型和成员方法的返回值类型,不一定都是定义类时的泛型。也可以是指定类型。
例如:
public class GenericityDemo<T> {
private String name;
private T age;
public void print(){
}
}
2. 在创建泛型类对象时,可以不传入泛型实参。此时泛型类中的成员变量和成员方法可以是任意类型的。
例如,ArrayList集合,在初始化的时候不传入泛型实参,则ArrayList集合中可以添加不同类型的元素。代码如下:
public class DemoMain {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("123"); //String 类型
arrayList.add(456); //Integer类型
arrayList.add('3'); //Character类型
}
}
2、泛型接口
定义: 使用泛型定义的接口,称为泛型接口。
格式: 和定义泛型类差不多。在正常定义接口的接口名后加即可。
例如:
interface Usb<T>{
T connect();
}
注意:
1. 实现接口时,如果不传入泛型实参,那么该实现类还是一个泛型类,就得满足泛型类的定义格式。
例如:
public class Computer<T> implements Usb<T>{
@override
T connect(){
System.out.println("接口已连接");
return null;
}
}
2. 如果实现接口时,传入了泛型实参,该类就和普通类定义格式一样。例如:
public class Computer implements Usb<boolean>{
@override
boolean connect(){
System.out.println("接口已连接");
return true;
}
}
3、泛型方法
定义: 使用了泛型定义的方法,称为泛型类。
格式: 在权限修饰符和返回值之间加<泛型变量名>,并且参数类型也得有泛型。例如:
public <T> String getName(T s){
return null;
}
注意:
1. 静态方法的泛型定义是在static关键字后面加上<泛型变量名>,而且静态方法无妨访问类上定义的泛型。例如:
public class GenericityDemo<T> {
private String name;
private T age;
public static<E> void print(E e){
}
}
如果类中的静态方法想直接使用类中的泛型,即static不加上<泛型变量名>,而形参上传入类上定义的泛型形参,那么编译时就会报错StaticGenerator cannot be refrenced from static context。例如:
public class GenericityDemo<T> {
private String name;
private T age;
//编译时报错:StaticGenerator cannot be refrenced from static context
public static void print(T t){
}
}
即静态方法无妨访问类上定义的泛型。
2. 即使在方法上定义的泛型名和类上定义的泛型名一样,他们也代表两个不同的泛型含义。例如:
public class GenericityDemo<T> {
private String name;
private T age;
//类上的泛型T和下面方法中的泛型T是不同的
public <T> String getName(T s){
return null;
}
}
4、通配符
格式: <?>,尖括号1中间只能是?。
用法: 在使用泛型类、泛型接口或者泛型方法时,如果还是不确定传入什么样的泛型实参,那么就可以用<?>代替,表示可以使用任何泛型实参。
例如:
pulic void print(GenericityDemo<?> gd){
}
在方法形参中使用通配符,只有当传进实参后,?的类型才确定。
注意:
1. <泛型变量名> 表示参数类型,在定义泛型类、泛型接口或者泛型方法时使用。
2. <?>表示通配符,在使用泛型类和泛型方法时使用,主要用于定义方法的形参。
泛型的上界: 传入的类型实参必须是指定类型的子类型。
1. 格式:<? extends 指定类型>,例如<? extends Number>。表示传入的泛型实参只能是Number类的孩子类。
泛型的下界 传入的类型实参必须是指定类型的祖先类。
1. 格式:< ?super 指定类型> ,例如< ?super Number>。表示传入的泛型实参只能是Number类的祖先类。
使用通配符的好处
1. 可以在调用方法时,传入不同泛型类型的实参。例如:
public class DemoMain {
public static void main(String[] args) {
GenericityDemo<String> genericityDemo = new GenericityDemo<>();
GenericityDemo<Integer> genericityDemo1 = new GenericityDemo<>();
print(genericityDemo);
print(genericityDemo1);
}
public static void print(GenericityDemo<?> t){
System.out.println(t.toString());
}
}
2. 将一个引用指向多个对象。例如:
public class DemoMain {
public static void main(String[] args) {
GenericityDemo<?> genericityDemo = new GenericityDemo<>();
GenericityDemo<String> genericityDemo1 = new GenericityDemo<>();
GenericityDemo<Integer> genericityDemo2 = new GenericityDemo<>();
genericityDemo = genericityDemo1;
genericityDemo = genericityDemo2;
//传入了指定类型后,就不可以指向不同泛型实参的对象。
genericityDemo1 = genericityDemo2;
}
}