泛型
1.概述
相当于把数据类型作为参数来进行传递。(泛型只能是引用数据类型)
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。
2.泛型类
2.1初识
public class Demo{
public static void main(String[] args) {
Box box = new Box();
box.s = new Integer(10);
((Integer)box.s).
}
}
class Box {
//用于存放数据
//存放的数据可以当作类中的一个成员变量来存储
//new Box()给s赋值
//String s;
//但要是想存放任意类型元素
Object s;
}
此刻我们来看看ArrayList是如何处理的
我们发现它的返回值类型就是E–相当于是元素的数据类型
2.2实操
2.2.1定义泛型
在类名后加<>,在<>中定义泛型,<>中的内容相当于泛型的名字,可以随便写。在泛型类中我们可以把这个泛型的名字当做一个数据类型来使用。
public class TestClass<T> {
//...
}
2.2.2使用泛型
public class TestClass<T> {
public void test(T t){
}
}
2.2.3泛型的确定
①创建对象时确定
在创建泛型类对象的时候确定之前定义的泛型代表什么数据类型。在定义泛型类对象的时候,在类名的后加<>,在其中写一个具体的数据类型。
public static void main(String[] args) {
TestClass<String> t = new TestClass();//指定了该对象的泛型T是String类型
t.test("三更草堂");//所以test方法的参数类型应该也是String类型
}
再回到我们Box例子
public class Demo{
public static void main(String[] args) {
Box<String> box = new Box<>();
box.put("嘿嘿");
Box<Integer> integerBox = new Box<>();
integerBox.put(1);
}
}
class Box <E>{
private E s;
//方法参数的类型就是元素的类型
public void put(E e){
this.s = e;
}
}
E只是指代而已,具体的类型在创建对象的时候确定,加入<>来确定。
②定义子类时确定
在定义子类的时候可以确定泛型。
public class SubClass extends TestClass<String> {
@Override
public void test(String s) {
}
}
这么写完后,父类当中的泛型T就变成字符串类型
public class Demo{
public static void main(String[] args) {
Box<String> box = new Box<>();
box.put("嘿嘿");
subBox subBox = new subBox();
subBox.put("ww");
}
}
class Box <E>{
private E s;
//方法参数的类型就是元素的类型
public void put(E e){
this.s = e;
}
}
//写完这句后,父类中put方法以及元素的类型都是字符串类型
class subBox extends Box<String>{
}
注意:我们在定义子类时也可以选择不确定泛型,让其在创建对象的时候在确定。写法如下
public class SubClass<T> extends TestClass<T> {
@Override
public void test(T t) {
super.test(t);
}
}
回到我们Box
public class Demo{
public static void main(String[] args) {
//String先传给subBox2 然后Box,最终put方法就是String
subBox2<String> subBox2 = new subBox2<>();
subBox2.put("2");
}
}
class Box <E>{
private E s;
//方法参数的类型就是元素的类型
public void put(E e){
this.s = e;
}
}
class subBox2<E> extends Box<E>{
}
接口就是在创建实现类对象的时候再去确定具体的泛型,与类是一样的
3.泛型方法
3.1定义泛型
在方法返回值类型的前面加<>,在<>中定义泛型,<>中的内容相当于泛型的名字,可以随便写。在该泛型方法中我们可以把这个泛型的名字当做一个数据类型来使用。
public static <T> T test(T t){
return t;
}
3.2使用泛型
在泛型方法中可以使用定义的泛型。并且我们一般是在参数列表中或者是返回值类型上使用到这个泛型。
3.3泛型的确定
在调用泛型方法的时候才真正确定之前定义的泛型代表什么数据类型。在调用泛型方法的时候,程序会根据你的调用自动推导泛型的具体类型。
public static void main(String[] args) {
Integer test = test(1);//这个1会做自动装箱
String s = test("三更草堂");
}
是用传入的参数来确定泛型类型
public static void main(String[] args) {
//定义一个String数组
String[] s = {"w"};
String[] array = getArray(s);
//数组动态初始化
Integer[] array1 = getArray(new Integer[0]);
}
public static <T> T[] getArray(T[] arr){
return arr;
}
输入方法名和参数s后.var即可得到String类型数组
4.泛型的上限/下限
4.1泛型限定的概念
我们在使用确定泛型的时候可以使用任意的引用数据类型去确定。但是在某些场景下我们要求这个泛型必须是某个类的子类或者是某个类的父类。这种情况下我们就需要用到泛型上限和泛型上限来限制泛型的范围。
4.2泛型上限
限制泛型必须是某个类或者是其子类。 <? extends 具体的类型>
比如:public static void test(List<? extends Person> t) 它的参数类型是List集合比如arraylist,linkedlist等,但集合可以传入任意的类型。写出这个后便是规定,我们再调用test方法的时候只能存入泛型为Person或者是Person子类的List集合对象。
Arraylist的繁星是people也可以添加student是因为可以自动向上转型,而泛型不存在自动向上转型。
4.3泛型下限
4.4注意事项
1.泛型上限可以在定义泛型类和方法参数上使用
public class Box {
E e;
}
2.泛型下限主要在方法参数上使用。
参考人:三更草堂
参考视频:https://www.bilibili.com/video/BV1ca411F7fr?p=23&spm_id_from=pageDriver