泛型
泛型的好处:安全、避免类型转换
泛型的使用形式有两种:泛型类/泛型接口、泛型方法
一、泛型类
语法格式
[修饰符] class 类名<泛型形参列表>{
}
[修饰符] interface 类名<泛型形参列表>{
}
public interface Collection<E> //<E>就是泛型形参列表
public class ArrayList<E> //<E>就是泛型形参列表
public class HashMap<K,V> //<K,V>就是泛型形参列表
要求:泛型是针对引用数据类型的,不能是各种基本数据类型。
public class UDFGeneric {
public static void main(String[] args) {
//语文老师
Student<String> chinese = new Student<String>();
//数学老师
Student<Double> math = new Student<Double>();
//英语老师
Student<Character> english = new Student<Character>();
}
}
/*
* 定义一个特殊的学生类,学生类包含两个属性:姓名、成绩
* 此时成绩的情况很复杂
* 语文老师表示学生时,成绩等级为:优秀、良好、及格、不及格
* 数学老师表示学生时,成绩为98.5,96.5....
* 英语老师在表示学生时,成绩为ABCD
*/
class Student<T>{
private String name;
private T score;
public Student() {
super();
}
public Student(String name, T score) {
super();
this.name = name;
this.score = score;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public T getScore() {
return score;
}
public void setScore(T score) {
this.score = score;
}
}
如何为泛型类、泛型接口指定泛型实参?
- 创建泛型类的对象时
Student<String> chinese = new Student<String>();
- 继承泛型类时可以指定泛型实参
- 实现泛型接口时可以指定泛型实参
class ChineseStudent extends Student<String>{
}
class Employee implements Comparable<Employee>{
private int id;
private String name;
public Employee(int id, String name) {
super();
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Employee [id=" + id + ", name=" + name + "]";
}
@Override
public int compareTo(Employee o) {
return this.id - o.id;
}
}
泛型类或泛型接口上的<泛型形参>
这个类型可以用在哪些地方?
- 可以用于属性、方法的数据形参、局部变量的类型
- 不能用于静态成员(因为静态成员可以通过类名调用,但是泛型在很多时候必须在实例化的时候指定)
class MyClass<T>{
private T t; //用于当作属性的数据类型
public MyClass(T t) { //用于当作方法的数据形参的类型
super();
this.t = t;
}
//可以用于返回值的类型
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
public String toString() {
return "MyClass [t=" + t + "]";
}
public void test() {
T t; //可以用于局部变量的类型
}
}
泛型类或泛型接口的泛型形参,可以设定上限
<T extends 上限> //T的类型实参只能是上限本身或者上限子类
<T extends 上限1 & 上限2 &上限3 .....>
如果在使用泛型类或者泛型接口时没有指定泛型实参,会怎么样?
这种现象为泛型被擦除。泛型被擦除后,泛型形参被解析为泛型形参的第一个上限类型。
- 如果没有指定泛型形参上限,按Object处理
- 如果制定了泛型形参的上限,就按照上限处理
- 如果有多个上限,按照最左边的第一个上限处理
在这里必须说明泛型形参有多个上限的情况
//T的实参要求,同时是Number的子类,还要实现Comparable和Serializable接口
class AClass<T extends Number & Comparable & Serializable>{
}
二、泛型方法
什么情况需要声明泛型方法?
-
如果某个静态方法想要使用泛型,需要单独设计
例如:
java.util.Arrays
数组工具类public static <T> List<T> asList(T... a)
-
如果泛型类型或者泛型接口上的泛型形参不适用于某一个方法(可以是静态的,也可以非静态),那么这个方法可以单独设计泛型
例如:
java.util.Collection<E>
public abstract <T> T[] toArray(T[] a)
泛型方法的语法格式
[修饰符] <泛型形参列表> 返回值类型 方法名([数据形参列表])
泛型方法的<泛型形参列表>这个类型就用于当前方法的形参类型、返回值类型、局部变量,和其他方法无关
泛型方法的类型形参什么时候指定类型实参
当调用方法时,编译器会根据方法的实参的类型,来确定泛型的实参类型。
List<Integer> asList = Arrays.asList(1,2,3,4,5);//根据1,2,3,4,5,可以确定是Integer类型
泛型方法的<泛型形参列表>中也可以指定上限
public <T extends Number> void test(T t){}
三、通配符
通配符:wildcard
形式:
<?> //代表可以是任意类型
<? extends 上限> //?代表是上限或者上限的子类
<? super 下限> //?代表是下限或者下限的父类
什么情况下使用通配符
/*
声明一个方法,这个方法可以用于遍历所有Collection系列的集合,此时因为Collection是一个泛型接口,Collection<E>,如果不指定<E>的具体类型,会报警告,对于这种情况,有两种选择,(一)抑制警告;(二)用?来表示任意类型
*/
public void print(Collection<?> c){
for (Object object : c){
System.out.println(object);
}
}
public void print(Collection<Object> c){
for (Object object : c){
System.out.println(object);
}
}
print(Collection<String> c);//这是错误的,一定会报错
Object
可以接受任意类型对象,但是不代表Collection<Object>
可以接收任意泛型实参的集合。
ArrayList<Object> obj = new ArrayList<String>;
//这是绝对错误的写法,这不是多态,这是乱写
所以通配符在某种意义上实现了泛型的多态