为什么需要泛型?
假如我们现在需要写一个排序方法,需要对整型数组,字符数组或者其它类型的数组进行排序操作,我们该如何实现?
由于对未来存储的数据类型并不确定,因此通过泛型将数据类型抽象起来,后续传入什么数据类型就可以存储什么数据类型。
对于上述需求,我们可以写一个泛型方法,在调用方法时将参数类型传递给泛型方法,就可以实现排序。
泛型的使用规则
泛型的定义
- 常用泛型表示:
字母 | 应用 |
---|---|
E | Element (在集合中使用,因为集合中存放的是元素) |
T | Type(Java 类) |
K | Key(键) |
V | Value(值) |
N | Number(数值类型) |
? | 表示不确定的 Java 类型 |
- 代码示例(以Java中的栈为例)
public class Stack<K> {
private K[] arr;
private int maxSize;
private int top;
public Stack(int maxSize) {
super();
this.maxSize = maxSize;
arr = (K[])new Object[maxSize];
top = -1;
}
public boolean isFull() {
return top == maxSize - 1;
}
public boolean isEmpty() {
return top == -1;
}
public void push(K num) {
if(isFull()) {
System.out.println("栈满!");
}else {
arr[++top] = num;
}
}
public K pop() {
if (isEmpty()) {
//System.out.println("栈空!");
throw new RuntimeException("栈空!");
} else {
K res = arr[top--];
return res;
}
}
}
使用语法
类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
jdk1.7之后,
new
后面的尖括号可以省略:类名<具体的数据类型> 对象名 = new 类名<>();
class 类名<泛型标识1,泛型标识2...> {
private 泛型标识1 变量名;
}
interface 接口名<泛型标识1,泛型标识2...> {
泛型标识1 方法名;
}
使用时需要注意:
- 关于定义:
- 泛型可以使数据类型参数化,让类的定义更加通用。
- 泛型只能使用类类型,不能使用基本类型(如
int
、double
、char
等)。基本类型想要使用泛型需要用包装类。 - 创建对象的时候,如果没有明确指明数据类型,不会报错,会按照
Object
类型处理。 - 统一泛型类,在逻辑上可以看成多个不同的类型,但是实际上是相同的。(参考下方代码测试)
Demo<Double> demo1 = new Demo<Double>(3.14);
Demo<String> demo2 = new Demo<String>("abc");
System.out.println(demo1.getClass() == demo2.getClass()); //true
- 关于继承:
5. 如果子类是泛型,那么子类的泛型要和父类保持一致(泛型的表示字母一致),如果父类没有明确指明类型,则父类按照Object
类处理。
6. 如果子类不是泛型,父类要明确写出类型,如果父类没有明确指明,还是按照Object
类来处理。
public class Child<K> extends Parent<K> {
}
public class Child extends Parent<String> {
}
- 关于实现类:
7. 如果实现类也是泛型,实现类和接口的泛型保持一致,如果接口没有明确指明类型,则接口按照Object
类处理。
8. 如果实现类不是泛型,接口要明确写出类型,如果没有,也是按照Object
类处理。