java泛型
在定义类时,不会给( 属性 方法的返回值 方法的参数定义数据类型,而在类对象创建时为其指定相应的数据类型是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
1.什么是泛型?
在定义类时,不会给 类中成员: ( 属性 方法的返回值 方法的参数 )定义数据类型,而在类对象创建时为其指定相应的数据类型
泛型:是JDK5中引入的特性,可以在编译阶段约束操作的数据类型,并进行检查。
泛型的格式:<数据类型>
常见的泛型-------集合中的泛型 : List <E>
注意:泛型只能支持引用数据类型
2.为什么使用泛型
举个栗子:定义一个姓名 和年龄
package com.demo.demo1;
public class student {
private String name;
private int age;
public student() {
}
public student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 获取
* @return name
*/
public String getName() {
return name;
}
/**
* 设置
* @param name
*/
public void setName(String name) {
this.name = name;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "student{name = " + name + ", age = " + age + "}";
}
}
当添加时,没有给集合限制类型,所有默认是Object类型,因为Object是所有类的父类
--自动向上转型
添加的时候,可以添加任意类型
package com.demo.demo1;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo {
public static void main(String[] args) {
//没有泛型的时候,集合如何储存数据
//1.创建集合的对象
ArrayList list= new ArrayList<>();
//2.添加数据
list.add(113);
list.add("小张");
list.add(new student("小丽",18));
//3.遍历集合获取每一个元素
Iterator it = list.iterator();
while (it.hasNext()){
Object obj = it.next();
System.out.println(obj);
}
}
}
迭代器遍历打印的结果
而我们想用string的length时候会报错,多态的弊端是不能使用子类的特有功能,强行使用会报错
试一下强转为String
package com.demo.demo1;
import java.util.ArrayList;
import java.util.Iterator;
public class Demo {
public static void main(String[] args) {
//没有泛型的时候,集合如何储存数据
//1.创建集合的对象
ArrayList list= new ArrayList<>();
//2.添加数据
list.add(113);
list.add("小张");
list.add(new student("小丽",18));
//3.遍历集合获取每一个元素
Iterator it = list.iterator();
while (it.hasNext()){
String str = (String) it.next();
//多态的弊端是不能访问子类的特有功能 比如现在获取字符串的长度就会报错
//obj.length();
str.length();
System.out.println(str);
}
}
}
而强转后也会报错,类型转换异常,Integer 不能强转为String类型 数据类型不安全问题
而强转为student 对象 字符和整数也都不能用了
集合加上String类型,也会报错
添加的数据要么全是String类型,要么全是Integer类型
package com.demo.a1_fanxing;
import java.util.Arrays;
/*
*
* 当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类
* */
public class MyArrayList <E>{
Object[]obj=new Object[10];
int size;
/*
添加的方法里不知道存什么数据可以使用E
E: 表示是不确定的类型,该类型在类名后面已经定义过了.
e:形参的名字,变量名
* */
public boolean add(E e){
obj[size]=e;
size++;
return true;
}
/**
* 返回值类型不能写object对象 ,表示返回值也要写不确定的类型
* @param index
* @return
*/
public E get(int index){
return (E)obj[index];
}
@Override
public String toString(){
return Arrays.toString(obj);
}
}
结论:
如果我们没有给集合指定类型,默认认为所有的数据类型都是object类型
此时可以往集合添加任意的数据类型。
带来一个坏处:我们在获取数据的时候,无法使用他的特有行为。
添加的数据要么全是String类型,要么全是Integer类型...等类型
所以java推出了泛型,可以在添加数据的时候就可以把类型进行统一,而且我们在获取数据的时候,也省的强转了,非常的方便
好处:
统一数据类型
把运行时期的问题提前到了编译期间,避免了强制类型转换可能出现的异常,因为在编译阶段类型就能确定下来
扩展知识:
java中的泛型是伪泛型,只在编译器有效,假如有一个泛型的集合是String类型,只要是String类型都可以添加集合当中,当数据添加的时候仅仅是在门口去检查是否符合String类型,符合就添加进去,当添加到里面的时候,集合还是会这些数据当成Object类型来处理,当往外获取的时候,集合的底层会把这些Object按着泛型类型去强转为对应的类型,编写的时候存在真正泛型,编译成字节码文件的时候,泛型就会消失,在java中有个专业名字,叫做"泛型的擦除"
因为在老版本的时候是没有泛型的,在jdk5才出来的,在jdk5以前都是当做Object类型,大家在使用的时候非常的不方便,又不可能去改以前的屎山源码,在原本的代码不动之上,在代码的门口加个限定,这样就解决了又不用修改代码,又统一了类型,两全其美,美滋滋
泛型的细节:
泛型中不能写基本数据类型,因为写了也没办法转成Object类型,比如int只能用它的父类Integer才能转成Object
指定泛型的具体类型后,传递数据时,可以传入该类类型或者其子类类型
如果不写泛型,类型默认是Object
3.如何定义泛型类?
泛型可以在很多地方进行定义
写在类后面称为泛型类,写在方法上面称为泛型方法,写在接口后面称为泛型接口
泛型类使用场景:当一个类中.某个变量的数据类型不确定时,就可以定义带有泛型的类
举个栗子:这里的<E>就表示不确定的类型,当创建该类对象时,E就确定了类型,此处E可以理解为变量,但是不是用来记录数据的,而是记录数据的类型,可以写成任意的:T丶E丶K丶V等
package com.demo.a1_fanxing;
import java.util.Arrays;
/*
*
* 当我在编写一个类的时候,如果不确定类型,那么这个类就可以定义为泛型类
* */
public class MyArrayList <E>{
Object[]obj=new Object[10];
int size;
/*
添加的方法里不知道存什么数据可以使用E
E: 表示是不确定的类型,该类型在类名后面已经定义过了.
e:形参的名字,变量名
* */
public boolean add(E e){
obj[size]=e;
size++;
return true;
}
/**
* 返回值类型不能写object对象 ,返回值也要写不确定的类型
* @param index
* @return
*/
public E get(int index){
return (E)obj[index];
}
@Override
public String toString(){
return Arrays.toString(obj);
}
}
注意:如果上边定义的<E>没有,那么方法里的E会报错,需要在类后面定义泛型
返回值的类型也要写成不确定的类型,不能写Object类型
创建对象
package com.demo.demo1;
public class Demo2 {
public static void main(String[] args) {
MyArrayList<String> list=new MyArrayList<>();
}
}
当添加时候只能添加字符串,形参变成了字符串
放入Integer类型就会报错
输出打印
如果类型Integer类型,那么就不能添加String类型
添加的时候显示只能添加Integer类型
使用get方法也是Integer类型
输出打印
4.如何定义泛型方法?
如果方法形参不确定有两种方案:
方案一:
使用类后面定义的泛型, 所有方法都能用
方案二:
在方法申明上定义自己的泛型,只有本方法能用
格式:
举例:此处T可以理解为变量,但是不是用来记录数据的,而是记录类型的,可以写成:T、E、K、V等,调用该方法时,T就确定类型
泛型方法的简单定义
[访问权限] <泛型标识> 泛型标识 方法名称 (泛型标识 参数名称) {
}
方法中形参类型不确定时.可以使用类名后面定义的泛型<E>
但是如果只有类中只有一个方法的形参不确定,就没必要在类上加上泛型,只需要在方法上加上泛型,称为泛型方法,但是其他的方法就不能用了,想用也加上与其对应的<E>
但是其他的方法就不能用了,想用也加上与其对应的<E> ,或者在类名后加上<E>
举个栗子:
定义一个工具类:ListUtil
类中定义一个静态方法allAll,用来添加多个集合的元素
package com.demo.demo1;
import java.util.ArrayList;
public class ListUtil {
private ListUtil(){}
//类中定义一个静态方法allAll,用来添加多个集合的元素
/*
*
* 参数一:集合
* 参数二~最后:要添加的元素
* */
public static<E> void addAll(ArrayList<E>list,E e1,E e2,E e3){
list.add(e1);
list.add(e2);
list.add(e3);
}
public void show(){
System.out.println("哦吼");
}
}
注意:
如果不确定集合添加的元素要加泛型<E>,但是不能直接写
要把泛型E写在方法上面,要写在修饰符后面,public static是两个要写在sttatic后面
没有调用,类型是Object类型
方法被调用,类型就能确认下来
Integer类型
想一直添加又不确定长度可以用可变参数
public static<E> void addAll(ArrayList<E>list,E ...e){
for (E element:e) {
list.add(element);
}
}
package com.demo.demo1;
import java.util.ArrayList;
/*
定义一个工具类:ListUtil
类中定义一个静态方法allAll,用来添加多个集合的元素
* */
public class Demo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
ListUtil.addAll(list, "aaa", "bbb", "ccc");
System.out.println(list);
ArrayList<Integer> list2 = new ArrayList<>();
ListUtil.addAll(list2,1,2,3);
System.out.println(list2);
ArrayList<Integer> list3 = new ArrayList<>();
ListUtil.addAll(list3,1,2,3,4,5,5,5,5,5,,5,5,5,5,5,5,5);
System.out.println(list3);
}
}
小细节:
泛型方法的定义与其所在的类是否是泛型类是没有任何关系的,所在的类可以是泛型类,也可以不是泛型类。
5.如何定义泛型接口
当一个接口中类型不确定可以使用泛型接口
重点:如何使用一个带泛型的接口
格式:
泛型接口的两种使用方式:
方案一:
使用类后给出具体类型
方案二:
实线类持续泛型,创建对象时再确定
方案一:
给出具体类型
创建一个实现类实现List接口 ,发现报错是因为List有泛型,Ctrl+B键跟进
需要加上类型
当实现接口定义类型的时候,那么它的添加方法,全都是与之对应的类型,
实现的方法有很多,只看它的add方法就会发现是String类型,
创建对象的时候就不用自己加泛型了
调用add方法时候,显示的是String
给其他类型就会报错
方案二:
实线类持续泛型,当实现类也没有明确是什么类型的时候可以使用此方法延续下来,创建对象时再确定
相当于创建了泛型类实现了带有泛型接口
此时发现方法都已经不确定了
创建的时候必须给其类型,,不给类型添加的时候就是Object类型
给其String类型,添加的时候也是String类型
package com.demo.demo1;
public class Demo4 {
public static void main(String[] args) {
/*
泛型接口的两种使用方法:
1.实现类给出具体的类型
2.实现类延续泛型,创建实现类对象时再确定类型
* */
MyArrayList2 list=new MyArrayList2();
list.add("123");
MyArrayList3<String>list2=new MyArrayList3<>();
list2.add("123");
}
}
结论:
当你实现类可以确定类型的时候,可以使用第一种,
当你实现类不确定类型的时候可以使用第二种
注意:
第二种调用的时候必须给其类型
6.泛型的继承和通配符
泛型本身不具备继承性,但是数据具备继承性
package com.demo.demo1;
import java.util.ArrayList;
public class Demo5 {
public static void main(String[] args) {
/*
* 泛型不具备继承性,但是数据具有继承性
* */
//创建集合的对象
ArrayList<Ye>list1=new ArrayList<>();
ArrayList<Fu>list2=new ArrayList<>();
ArrayList<Zi>list3=new ArrayList<>();
//调用method方法
method(list1);
// method(list2);
// method(list3);
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
* */
public static void method(ArrayList<Ye> list){
}
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
当调用method方法的时候,list2和list3报错
因为泛型本身不具备继承性,泛型里面写的是什么类型,那么只能传递什么类型的数据
但是数据具有继承性
package com.demo.a1_fanxing;
import java.util.ArrayList;
public class Demo5 {
public static void main(String[] args) {
/*
* 泛型不具备继承性,但是数据具有继承性
* */
//创建集合的对象
ArrayList<Ye>list1=new ArrayList<>();
ArrayList<Fu>list2=new ArrayList<>();
ArrayList<Zi>list3=new ArrayList<>();
//调用method方法
//method(list1);
//method(list2);
//method(list3);
list1.add(new Ye());
list1.add(new Fu());
list1.add(new Zi());
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
* */
public static void method(ArrayList<Ye> list){
}
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
下面举个详细例子
希望:本方法虽然不确定类型,但是以后我希望只能传递a b c
弊端:
利用泛型方法有一个小弊端,此时他还可以接受任意的数据类型
package com.demo.demo1;
import java.util.ArrayList;
public class Demo6 {
public static void main(String[] args) {
/*
* 需求:
* 定义一个方法,形参是一个集合,但是集合中的数据类型不确定。
* */
//创建集合的对象
ArrayList<Ye>list1=new ArrayList<>();
ArrayList<Fu>list2=new ArrayList<>();
ArrayList<Zi>list3=new ArrayList<>();
ArrayList<Student>list4=new ArrayList<>();
method(list1);
method(list2);
method(list3);
method(list4);
}
/*
* 此时,泛型里面写的是什么类型,那么只能传递什么类型的数据
*
* 弊端:
* 利用泛型方法有一个小弊端,此时他还可以接受任意的数据类型
* Ye Fu Zi Student
*
* 希望:
* 本方法虽然不确定类型,但是以后我希望只能传递Ye Fu Zi
*
* 此时我们就可以使用泛型的通配符:
* ? 也代表不确定的类型
* 它可以进行类型的限定
* ? extends E: 表示可以传递E或者E所有的子类类型
* ? super E: 表示可以传递E或者E所有的父类类型
* */
public static<E> void method(ArrayList<E> list){
}
}
class Ye{}
class Fu extends Ye{}
class Zi extends Fu{}
class Student{}
此时我们就可以使用泛型的通配符
? 也表示不确定的类型
它可以进行类型的限定
? extends E: 表示可以传递E 或者 E所有的子类类型
? super E:表示可以传递E或者E所有的父类类型
[设置上限]
声明对象: 类名称<? extends 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 extends 类>{}
[设置下限]
声明对象: 类名称<? super 类> 对象名称;
定义类: [访问权限] 类名称<泛型标识 super 类>{}
使用?效果和E是一样的,使用?不用在修饰符后加<E>
? extends E: 表示可以传递E 或者 E所有的子类类型,此时的E就需要明确类型了
? super E:表示可以传递E或者E所有的父类类型
package com.demo.a1_fanxing;
import java.util.ArrayList;
public class Demo7 {
public static void main(String[] args) {
/*
* 需求:
* 定义一个方法,形参是一个集合,但是集合中的数据类型不确定。
* */
//创建集合的对象
ArrayList<a1>list1=new ArrayList<>();
ArrayList<a2>list2=new ArrayList<>();
ArrayList<a3>list3=new ArrayList<>();
ArrayList<Student2>list4=new ArrayList<>();
method(list1);
method(list2);
// method(list3);
// method(list4);
}
/*
* 希望:
* 本方法虽然不确定类型,但是以后我希望只能传递a1 a2 a3
*
* 此时我们就可以使用泛型的通配符:
* ? 也代表不确定的类型
* 他可以进行类型的限定
* ? extends E: 表示可以传递E或者E所有的子类类型
* ? super E: 表示可以传递E或者额E所有的父类类型
* */
public static void method(ArrayList<? super a2> list){
}
}
class a1{}
class a2 extends a1{}
class a3 extends a2{}
class Student2{}
结论:
1.如果我们在定义类、方法、接口的时候,如果类型不确定,就可以定义泛型类、泛型方法、泛型接口。
2.如果类型不确定,但是能知道以后只能传递某个继承体系中的,就可以泛型的通配符泛型的通配符:
关键点:可以限定类型的范围。
不想限定范围就可以用之前的泛型类,泛型方法,泛型接口就可以了
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)