泛型
1、泛型含义
泛型是一种类型参数,专门用来保存类型用的。
例如ArrayList,这个E就是所谓的泛型了。使用ArrayList时,只要给E指定某一个类型,里面所有用到泛型的地方都会被指定对应的类型。
2、使用泛型的好处
不用泛型带来的问题
集合若不指定泛型,默认就是Object。存储的元素类型自动提升为Object类型,获取元素时得到的都是Object,若要调用特有的方法需要转型,这样会给变成带来很多麻烦。
使用泛型带来的好处
可以在编译时对类型做出判断,不免不必要的类型转换操作,精简代码,也避免了因为类型转换导致的错误。
注意:泛型在代码运行时,泛型会被擦除,我们可以实现在代码运行过程中添加其他类型的数据到集合中,所以泛型是使用在编写时期的技术方式。
3、泛型的使用
3.1、泛型类
-
定义格式
在类型名后面加上一对尖括号,里面定义泛型。一般使用一个英文大写字母,如果有多个泛型使用逗号隔开。
public class 类名<泛型>{//类型内部,可以把泛型当作是某一种类型使用 }
-
泛型的确定
当我们不确定类中的某个属性使用什么类型时,可以用泛型表示
public class 类名<TG>{private TG 变量; }
在我们创建泛型类对象时,明确泛型类型
泛型类<Double> 对象 = new 泛型类<Double>(); //此时泛型类中的成员变量是Double类型
-
代码示例
//当不确定类中成员变量使用什么类型时可以用泛型来表示
class TgClass<T>{private T demo;public T getDemo() {return demo;}public void setDemo(T demo) {this.demo = demo;}
}public class Demo1 {public static void main(String[] args) {//创建对象,指定泛型类型为DoubleTgClass<Double> d = new TgClass<Double>();d.setDemo(2319848.89901);System.out.println(d.getDemo());}
}
3.2、泛型接口
-
定义格式
在接口名后面加上一对尖括号,里面定义泛型。一般使用一个英文大写字母,如果有多个泛型使用逗号隔开。
public interface 接口名<T>{}
-
泛型确定
当定义接口时,其内部方法中的参数或者返回值类型不确定时,就可以使用泛型代替了。
比如这样
public interface 泛型接口<T>{public void demo(T x); }
可以在实现类,实现接口时,确定接口中的泛型的类型(情况一),如果实现类和接口不指定具体类型,继续使用泛型指定变成含有泛型的类使用(情况二)。
比如这样
//情况一 public class 子类 implements 泛型接口<String>{public void demo(String x){} }//情况二 public class 子类<T> implements 泛型接口<T>{//方法重写public void demo(T a){} } 子类<String> 对象 = new 子类<>();//创建子类对象时明确泛型的类型
-
代码示例
定义父接口
public interface Father<T> {public void play(T x);
}
情况一
public class SonImp1 implements Father<Integer>{@Overridepublic void play(Integer x) {System.out.println(x + "参数类型是Integer");}
}public class Test1 {public static void main(String[] args) {SonImp1 sonImp1 = new SonImp1();sonImp1.play(999);}
}
情况二
public class SonImp2<T> implements Father<T>{@Overridepublic void play(T x) {System.out.println(x + "参数类型是String");}
}public class test2 {public static void main(String[] args) {SonImp2<String> sonImp2 = new SonImp2<>();sonImp2.play("哈哈哈");}
}
3.3、泛型方法
-
语法格式
修饰符号 <泛型> 返回值类型 方法名(泛型 参数1, 泛型 参数2,...){//方法体 }
-
泛型确定
如果当前类没有声明泛型类,但该方法中的方法参数或者方法返回值不确定时,可以使用泛型类
public <T> void demo(T x, Ty){}
调用含有泛型的方法时,传入的数据其类型就是泛型的类型
4、泛型通配符
4.1、泛型通配符介绍
当我们对泛型的类型确定不了了,而想要表达的是任意类型,可以使用泛型通配符给定。符号就是一个问号“?”,表示任意类型,用来给泛型指定的一种通配符。
4.2、泛型统配符使用
泛型统配符搭配集合使用一般在方法的参数中比较常见。
我们可以来个示例:
假如有一个Person父类
public class Person {public int age;public String name;
}
Person有两个子类Student和Teacher,这时候我们需要创建一个集合,向这个集合中添加指定元素类型,那么我们可以这样做
ArrayList<Student> array = new ArrayList<>();
ArrayList<Teacher> array = new ArrayList<>();
那还有什么方式向集合中添加指定元素类型呢?这时候,有人可能会联想到多态,那么我们是不是还可以这样
ArrayList<Person> array = new ArrayList<Student>();
很遗憾,java中是不允许这样做的,java中的泛型没有多态这个概念,正确的做法是这样
//使用通配符
ArrayList<?> array = new ArrayList<Student>();
这样做有什么好处嘞?我们试想,现在有两个方法如下
public static void demoStu(ArrayList<Student> array){}public static void demoStu(ArrayList<Teacher> array){}
假如两个方法中的操作相同,只是传递参数的类型有差别,这时候上面的代码就显得有些冗余了,那么这时候,统配符就可以解决此问题
public static void demoStu(ArrayList<?> array){}
这样做之后,无论我们传递的参数是ArrayList< Student>类型,还是ArrayList< Teacher>类型,方法都能接手参数,不会发生报错。
5、受限泛型
受限泛型是指,在使用通配符的过程中,对泛型做了约束,给泛型指定类型时,只能是某个类型父类型或者子类型。
5.1、泛型的下限
只能是某一类型,及其父类型,其他类型不支持。
<? super 类型><? super 最小类型>
//?可以是最小类型也可以是父类型
5.2、泛型的上限
只能是某一类型,及其子类型,其他类型不支持。
<? extends 类型><? extends 最大类型>
//?可以是最大类型,也可以是子类型
5.3、代码示例
4.2中的示例方法我们现在还能这样写
public static void demo(ArrayList<? extends Person> array){}