📋 个人简介
- 💖 作者简介:大家好,我是阿牛,全栈领域优质创作者。😜
- 📝 个人主页:馆主阿牛🔥
- 🎉 支持我:点赞👍+收藏⭐️+留言📝
- 📣 系列专栏:java 小白到高手的蜕变🍁
- 💬格言:要成为光,因为有怕黑的人!🔥
前言
java泛型可以提高java程序的类型安全,提高程序的可靠性,并且在开发中也较常用,因此还是要简单学习一下!
为什么要有java泛型
举个例子:我们在写代码时,为了满足程序的可靠性,往往结合面向对象的多态性,使用Object类型的参数做方法的参数,在这个方法里用instanceof做类型判断,不满足还要抛出异常,但使用泛型可以避免这种情况,传入不符合类型的参数,编辑器就会报错,即编译不通过!帮助我们解决了类型强转问题,提高效率!
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的对象,所以在JDK1.5之前只能把元素类型设计为 Object ,JDK1.5之后使用泛型来解决。因为这个时候除了元素的类型不确定,其他的部分是确定的,例如关于这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计成一个参数,这个类型参数叫做泛型。 Collection < E >, List < E >,ArrayList < E >这个<E >就是类型参数,即泛型。
java泛型的概念
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这个类型参数将在使用时(例如,继承或实现这个接口,用这个类型声明变量、创建对象时)确定(即传入实
际的类型参数,也称为类型实参)。
从JDK1.5以后, Java 引入了"参数化类型"的概念,允许我们在创建集合时再指定集合元素的类型,正如: List<string>,这表明该 List 只能保存字符串类型的对象。JDK1.5改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。
java泛型的使用
不使用泛型,默认元素是Object类型!
使用后:
在List中使用
例如:
ArrayList<String> strings = new ArrayList<String>();
表明这个ArrayList只能存储String类型的数据!
实例化后,添加数据add(E e)=》add(String e),这里的E对应的类型就是你创建对象时传入的类型!
注意:泛型的类型必须是类,不能是基本数据类型,如果是基本数据类型,则要其对应的包装类!
ArrayList<Integer> strings = new ArrayList<Integer>();
此时我就可以直接遍历,不用再强制转换为int类型,这就是泛型的好处!
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<Integer>();
arr.add(18);
arr.add(12);
arr.add(5);
for (Integer item:arr){
System.out.println(item);
}
}
在Map中使用
例如:
HashMap<String, Integer> map = new HashMap<>();
map.put("aniu",666);
分别指明key和value的类型!
泛型的嵌套
HashMap<String, Integer> map = new HashMap<>();
map.put("aniu",666);
map.put("Tom",555);
//泛型嵌套
Set<Map.Entry<String, Integer>> entries = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entries.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
map.entrySet()返回Set,Set的元素类型Map.Entry,这个Entry是Map的一个内部接口,Entry里面存储key-value,分别是String和Integer类型,这就是泛型的嵌套,关于这个Entry,了解HsahMap源码的应该都知道!
由上面泛型的使用可以总结出:在实例化集合类时,可以指明具体的泛型类型,指明完以后,在集合类或者接口中,内部结构使用到类的泛型的位置,都指定为实例化的泛型类型!如果实例化时没有指明泛型的类型,默认为Object类型!
自定义泛型类
class People<T>{
T id;
String name;
int age;
public People(T id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public T getId() {
return id;
}
public void setId(T id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
如上形式,我自定义了一个泛型类,将其中的id类型定义为T,也就是我们实例化时自定义id的类型!
public static void main(String[] args) {
People<Integer> P = new People<Integer>(1,"aniu",21);
System.out.println(P.toString());
}
我们再来看看继承写法:
方式一:子类不是泛型类
class Stu extends People<String>{
public Stu(String id, String name, int age) {
super(id, name, age);
}
}
// 由于子类在继承带泛型的父类时,指明了泛型类型,则实例化子类对象时,不需要指明泛型
Stu stu = new Stu("1","aniu",20);
System.out.println(stu.toString());
方式二:子类也是泛型类
class Stu1<T> extends People<T>{
public Stu1(T id, String name, int age) {
super(id, name, age);
}
}
此时父类的泛型在子类保留
Stu1<String> stu1 = new Stu1<String>("1","aniu",20);
泛型方法
泛型方法和泛型类没有关系,泛型方法是指在方法中出现了泛型的结构,泛型参数与类的泛型参数没有关系,是不同的参数,换句话说:泛型方法所在的类是不是泛型类没有关系!
// 将数组转换为List
public <E> List<E> ArrToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e:arr){
list.add(e);
}
return list;
}
需要注意的是:泛型方法可以申明为静态的,因为泛型参数是在调用方法时确定的!
public static <E> List<E> ArrToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e:arr){
list.add(e);
}
return list;
}
通配符的使用
我们知道,例如:List<Object>和List<String>这种泛型的写法不再具有子父类的关系,他们不能相互赋值,但是有时我们需要通用的方法,这个方法的参数可以是两种泛型!我们应该怎么写呢,这就涉及到通配符?的使用了,你可以将List<?>当成List<Object>和List<String>的父类!
List<Object> list1 = new ArrayList<>();
List<String> list2 = new ArrayList<>();
List<?> list = null;
// 不能赋值
// list1 = list2;
// 此时赋值不会出现问题
list = list1;
list = list2;
public void print(List<?> list){
}
总结:
1.假设类A是类B的父类,则O<A>和O<B>是没有关系的,二者共同的父类是O<?> 。
2.对于List<?>不能向其中添加数据,除了添加null;但可以读取,读取的数据类型是Object。
有限制条件的通配符使用
主要就是<? extends …>和<? super …>
List<? extends People> list1 = null;
List<? super People> list2 = null;
List<Stu> list3 = null;
List<People> list4 = null;
List<Object> list5 = null;
//对于赋值给list1
// 可以赋值
list1 = list3;
list1 = list4;
// 不能赋值
//list1 = list5;
//对于赋值给list2
// 不可以赋值
//list2 = list3;
// 可以赋值
list2 = list4;
list2 = list5;
即<? extends …>相当于<=;<? super …>相当于>=。
这一块数据的读写赋值范围要注意,这里不再多说!
注意点总结
1.泛型类可能有多个<E1,E2,E3>
2.泛型类的构造器如下: public People ()
错误的: public Peoples < E >()
3.实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4.泛型不同的引用不能相互赋值。 尽管在编译时 ArrayList <String>和 ArrayList <lnteger>是两种类型,但是,在运行时只有一个 ArrayList 被加载到 JVM 中。
5.泛型如果不指定,将被擦除,泛型对应的类型均按照 Object 处理,但不等价于 Object 。经验:泛型要使用一路都用。要不用,一路都不要用。
6.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
7.jdk1.7泛型的简化操作: ArrayList < String> list = new ArrayList <>();
8.泛型的指定中不能使用基本数据类型,可以使用包装类替换。
9.在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。(泛型创建对象时才指明类型)
10.异常类不能是泛型的。
11.不能使用 new E[] 。但是可以: E [ ] elements =(E[ ]) new Object [capacity ]; 参考: ArrayList 源码中声明: Object elementData[ ] ,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型。
结语
如果你觉得博主写的还不错的话,可以关注一下当前专栏,博主会更完这个系列的哦!也欢迎订阅博主的其他好的专栏。
🏰系列专栏
👉软磨 css
👉硬泡 javascript
👉flask框架快速入门