前言
没有泛型的时候,我们声明的 List 集合默认是可以存储任意类型元素的,乍一看你可能还会觉得挺好,这样功能强大,啥类型都可以存储......但是开发的时候由于不知道集合中元素的确切类型,遍历的时候我们拿到的 item 其实是 Object 类型,如果要使用就必须强转,强转就必须得判断当前元素的具体类型,否则直接使用强转很可能会发生类型转换异常。这样就会让开发很不方便,每次都要额外做判断工作。
1.泛型概念
我的理解是:泛型就是把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。
参数化类型,把类型当作是参数一样传递,Object<数据类型>这里面只能是引用类型不能是基本类型。
1.为什么泛型里面数据类型不能是基本类型呢?
因为虚拟机在编译时会把带泛型的转换成Object类型,而基本类型不属于Object类型,所以泛型里面数据类型不能是基本类型。
2.为什么要使用泛型?
1.这个编译期安全是体现在,就是加入我们创建一个集合,用来存储int类型,但是不小心混进String类型,但是我们对这个集合的方法都是针对int类型,所以编译时会报错。
有泛型,我们就不会混进String数据。
2.创建集合,默认是Object类型的。我们不知道类型,加入存进去String,强转是int,tm不就抛异常了。
3.同一个类,能操作多种数据类型,怎么说呢?我们创建泛型类时,能自定义类型参数穿进去。
泛型只存在与编译期。
卧槽,再说一下,上面编译期的概念:编写代码 -- .class -- 代码运行
这其中,编写代码 -- .class,同属于编译期,因此我们编写代码,如果指定泛型类型,但是写出错误,编译器(编译器)给我们报错。
2.泛型的使用
可以是任意字母,只是比较个人比较习惯用 T 。
泛型有三种实用方式
泛型类:
public class Test<T>}{}
T表示未知类型泛型接口:
public interface Test<T>{}
和定义类一样泛型方法:
public <T> void Test(T name){}
// 泛型类中的<T>就没作用了
class Dog<T> implements Animal{
public static void main(String[] args) {
System.out.println(getStr("qhx"));
System.out.println(getStr(1));
}
public static <T> T getStr(T str){ // 泛型方法中的<T> ,就是这个方法中的T属于他管。
return str;
}
}
泛型统配父的使用
1.无边界通配符
ArrayList<?> objects = new ArrayList<>(); // 表明我们list可以存储任何类型,但是实际上编译之后就是Object对象
2.上边界通配符
public interface Animal {
}
class Dog implements Animal{
public static void main(String[] args) {
ArrayList<? extends Animal> objects = new ArrayList<>(); // 用extends关键字表明必须是Animal的子类或者实现类
}
}
3.下边界通配符
public interface Animal {
}
class Dog implements Animal{
public static void main(String[] args) {
ArrayList<? super Dog> objects = new ArrayList<>(); // 用super关键字表明必须是Dog父类或者接口
}
}