为什么我们需要泛型
1.适用于多种数据类型执行相同的代码
2.使用了泛型之后在编码的过程中可以指定我们数据类型,而不需要进行强制类型转换以及如果说我们插入错误的数据类型在插入的时候就可以发现。
泛型的定义
参数化的类型。
怎么来定义自己的泛型
泛型类,泛型方法,泛型接口…都是可以的。
泛型类如下
public class NormalGeneric<T> {
private T data;
public NormalGeneric(){
}
//虽然在方法中使用了泛型,但是这并不是一个泛型方法
//这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型
//所以在这个方法中才可以继续使用T这个泛型
public T getData(){
return data;
}
/**
这才是一个真正的泛型方法,首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
这个T可以出现在这个泛型方法的任意位置
泛型的数量也可以为任意多个
public <T,K> K showKeyName(NormalGeneric<T> container){
...
}
*/
public void setData(T data){
this.data=data;
}
public static void main(String[] args){
NormalGeneric<String> normalGeneric=new NormalGeneric<>();
normalGeneric.setData("OK");
System.out.println(normalGeneric.getData());
}
}
泛型接口
public interface Genertor<T> {
public T next();
}
如何来实现呢,可以有两种方式
public class ImplGenertor<T> implements Genertor<T> {
@Override
public T next() {
return null;
}
}
上面的泛型类继承泛型接口,本身和一般的泛型类的实现没有太大差别。如果清楚自己需要的数据类型,可以直接用如下的方式实现。
public class ImplGenertor2 implements Genertor<String>{
@Override
public String next() {
return null;
}
}
需要注意的是,在普通类里面,同样可以存在需要的泛型方法
public class GenericMethod {
public <T> T genericMethod(T...a){
return a[a.length/2];
}
public void test(int x,int y){
System.out.println(x+y);
}
public static void main(String[] args){
GenericMethod genericMethod=new GenericMethod();
genericMethod.test(23,123);
System.out.println(genericMethod.<String>genericMethod("mark","av","lance"));
System.out.println(genericMethod.genericMethod(12,34,45));
}
}
如下一个例子,更加深入理解泛型
public class GenericMethod3 {
static class Fruit{
@Override
public String toString(){
return "fruit";
}
}
static class Apple extends Fruit{
@Override
public String toString(){
return "Apple";
}
}
static class Person{
@Override
public String toString(){
return "Person";
}
}
static class GenerateTest<T>{
public void show_1(T t){
}
/*
在泛型类中声明了一个泛型E,使用泛型E,这种泛型E可以为任意类型。
由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型
编译器也能正确识别泛型方法中识别的泛型
*/
public <E> void show_3(E t){
System.out.println(t.toString());
}
/*
注意这里T是一个全新的类型,可以与泛型类中声明的T不是同一种类型
*/
public <T> void show_2(T t){
System.out.println(t.toString());
}
}
public static void main(String[] args){
Apple apple=new Apple();
Person person=new Person();
GenerateTest<Fruit> generateTest=new GenerateTest<>();
generateTest.show_1(apple);
generateTest.show_2(person);
generateTest.show_3(person);
}
}
限定类型变量
怎么保证执行的泛型的类型下一定会有有某种方法或者遵循某种规则呢?
比如下面的例子,怎么保证一定会有compareTo这个方法呢?
添加如下继承关系
public static <T extends Comparable> T min(T a,T b){
if(a.compareTo(b)>0)
return a;
else
return b;
}
这个T表示绑定类型的子类型,Comparable则表示绑定类型。
注意,这个限定类型可以是类,也可以是接口,但是如果类与接口混用,类必须写到前面,并且类只能有一个。
public static <T extends ArrayList&Comparable> T min(T a, T b){
if(a.compareTo(b)>0)
return a;
else
return b;
}
泛型类同样适用。
注意:不能实例化类型变量
静态域或者方法里不能引用类型变量
在这里插入图片描述
泛型中的约束和局限性
接着前面那个例子
关于异常情况:
泛型类型的继承规则
例:如果Employee和Worker是继承关系,Pair是一个泛型类,Pair和Pair没有任何继承关系。
泛型类可以继承或者扩展其他泛型类,比如List和ArrayList
通配符类型
public static void print2(Genertor<? extends Fruit> p){
}
public static void print2(Genertor<? super Apple> p){
}
GenericType<? supper Apple> x=new GenericType<>();
x.setData(new Apple());
x.setData(new HongFuShi());//Apple的子类
x.setData(new Fruit());//报错,只能set Apple的子类,因为对于setData来说,本身不知道传入的类型,但是Apple以及Apple的子类可以安全的转型为Apple
Object data=x.getData //返回的是Apple的超类
上面那串代码extends表示传入这个参数的上界只能是Fruit,也就是说传入参数的类型只能是Fruit的子类或者它本身。
而super则规定了下界,传入参数的类型只能是Apple的夫类或本身。
注意如上例子set和get的区别。
extends可以安全的返回数据,super可以安全的写入数据。