泛型
泛型的概念
jdk1.5提供的一个内容,泛型实际上就是将数据类型作为一个参数。
1.理解什么是泛型?
对比一下:
void method(int num){}==>num称之为参数(将数据作为一个参数)
调用method方法只能传递int类型的数据(只能传递int类型数据)
如果想要通过一个方法能够传递不同的数据类型(重载可以实现,泛型实现)
泛型:所谓泛型与上述不同,是将数据类型作为一个参数,编写方法或者类的时候,不需要声明其数据类型。
void method(? num)
2.为什么要用到泛型?
比如使用动态数组的时候?
int[] num=new int[10]
....
这样的动态数组只能放置int类型的数据
升级
Object[] obj=new Object[10];
obj[0]=1;//默认进行
obj[1]=new Student();//默认进行
这样我们的obj可以放置任意类型的数据
编译期间方任意内容都可以编译通过
Student stu=(Student)obj[index]; //运行的时候检查类型,存在类型转换异常的风险。。。
今天提供泛型好处:
<1>.将类型的判断从运行期,提前到了编译期间(编译期间就会检查类型,如果类型不对直接编译不通过)
<2>.能够避免运行的时候出现类型转换异常的风险。。。
泛型类
基本格式:
public class 类名<泛型参数1,泛型参数2,...>{
private 泛型参数1 属性名1;
private 泛型参数2 属性名2;
....
}
说明:
一般来说:泛型参数会使用一些单个大写字母表示:K,T,V,E,...(理论上还可以使用其他的字母)
注意事项:
<1>.创建对象的时候声明数据类型,声明的是什么类型就只能存放对应类型的数据。
<2>.如果创建对象的时候没有声明具体的数据类型,那么默认类型为Object;
<3>.定义的类型必须是引用类型,不能基本数据类型。
<4>.同一个泛型类,创建多个泛型类型不同的对象,最终这多个对象属于同一个类型。
代码实现:
设计泛型类型:
public class Generic<E> {
private E key;
public Generic() {
super();
}
public Generic(E key) {
super();
this.key = key;
}
public E getKey() {
return key;
}
public void setKey(E key) {
this.key = key;
}
@Override
public String toString() {
return "Generic [key=" + key + "]";
}
}
测试:
public static void main(String[] args) {
Generic<Integer> g1=new Generic<Integer>();//Generic g1=new Generic();
//1.编译的时候就会检查类型,只能存放整数类型的数据
g1.setKey(123);
System.out.println(g1);
Generic<String> g2=new Generic<>();//Generic g2=new Generic();
g2.setKey("admin");
System.out.println(g2);
//2.没有指定类型,那么默认可以存放任意类型的数据...
Generic g3=new Generic();
g3.setKey(123.456);
System.out.println(g3);
//1.g1,g2,g3属于同类吗? 是同类型
//利用反射查看对象的数据类型
System.out.println(g1.getClass().getSimpleName());
System.out.println(g2.getClass().getSimpleName());
System.out.println(g3.getClass().getSimpleName());
}
泛型接口
public interface 接口名<类型参数1,类型参数2,...>{
}
//1.非泛型类实现泛型接口的时候,必须指定类型,非泛型接口继承泛型接口的时候也必须指定类型
public class MyLinkedList implements MyList<String>{//==>必须指定类型
@Override
public void add(String ele) {
}
}
public interface MySet extends MyCollection<String>{
}
//2.泛型类 实现泛型接口的时候可以不指定泛型的类型 ,或者泛型接口也可以不指定类型
代码实现:
public interface MyCollection<E> {
void add(E ele);
}
继承
public interface MyList<E> extends MyCollection<E>{
}
实现
public class MyArrayList<E> implements MyList<E>{
@Override
public void add(E ele) {
System.out.println(ele);
}
}
总结:对于泛型类与泛型接口去继承或者实现另一个泛型接口的时候,子类泛型参数必须包含父类泛型参数
子类的泛型参数可以有多个...
泛型方法
泛型方法的基本语法:
public <参数类型1> void/其他返回值 方法名(参数类型1 参数名...){}
//注意1:不是泛型方法,是成员方法
public E getKey() {
return key;
}
//注意2:静态的方法中使用了泛型参数,那么静态方法必须是泛型方法
public static <T> void method(T t){
}
代码实现:
public class Generic<K,V> {
private K key;
private V value;
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
//1.泛型方法(遵循就近原则)
//泛型方法中定义的泛型参数可以与类中的泛型参数同名,但是 使用的时候方法的泛型参数与类的泛型参数不是同一个。
//调用方法的时候去确定方法泛型参数的类型。
public <K>void method1(K key) {// 泛型方法的 定义的泛型参数(与类上面的泛型参数无关)
System.out.println("普通的泛型方法:"+key);
}
//2.普通方法可以直接获取类上面的泛型参数
public void method2(K key) {//K 类的泛型参数
System.out.println("泛型类中的普通方法:"+key);
}
//3.静态方法如果想得到泛型参数必须 变成泛型方法。
public static<K> void method3(K key) {//静态方法拿不到类的泛型参数
System.out.println("静态的泛型方法:"+key);
}
@Override
public String toString() {
return "Generic [key=" + key + ", value=" + value + "]";
}
}
测试:
public static void main(String[] args) {
Generic<String, String> g1=new Generic<>();
g1.setKey("jack");
g1.setValue("abc");
//1.普通泛型方法(方法参数类型不受类参数类型的影响)
g1.method1(200);
//2.普通方法 使用了类的泛型参数,必须与类的泛型参数相同
g1.method2("admin");
//3.静态泛型方法(方法参数类型不受类参数类型的影响)
Generic.method3(123.456);
}
泛型通配符
?:泛型通配符(可以匹配任意类型)
1.泛型通配符向上修饰 (当前类以及其子类)
? extends 应用类型
2.泛型通配符向下修饰 (当前类以及父类)
? super 应用类型
---------------------------------------------------------------------------------
public static void main(String[] args) {
ArrayList<Animal> listAnimal=new ArrayList<>();//动物
ArrayList<Pet> listPet=new ArrayList<>();//宠物
ArrayList<Cat> listCat=new ArrayList<>();//小猫
ArrayList<Dog> listDog=new ArrayList<>();//小狗
//1.?:泛型参数类型任意
method1(listAnimal);
method1(listPet);
method1(listCat);
method1(listDog);
//2.? super Cat:Cat类,以及其父类以及其父类的父类都可以放(上级类都可以)。。。
method2(listCat);
method2(listPet);
method2(listAnimal);
//3.? extends Pet类 可以是当前类以及其后代类。。。
method3(listPet);
method3(listCat);
method3(listDog);
//4.只能传递Animal类型
method4(listAnimal);
}
//1.可以放任意类型泛型参数
public static void method1(ArrayList<?> list) {
System.out.println(list);
}
//2.泛型参数类型只能放当前类以及其父类(Cat类以及其父类) 上级类
public static void method2(ArrayList<? super Cat> list) {
System.out.println(list);
}
//3.泛型参数只能是当前类,以及其下级类(后代类)
public static void method3(ArrayList<? extends Pet> list) {
System.out.println(list);
}
//4.指定类型
public static void method4(ArrayList<Animal> list) {
System.out.println(list);
}
泛型的嵌套
public class Generic<T>{
}
T:类型的参数(可以是任意引用类型)
Generic是什么类型? 引用类型
Generic<Generic<String>> :泛型的嵌套
以ArrayList<E> 为例子:
也可以表示为:ArrayList<ArrayList<String>> list=...
list.add(ArrayList元素);
代码示例:
public static void main(String[] args) {
ArrayList<ArrayList<String>> list=new ArrayList<>();
//list里面只能添加ArrayList类型的元素
ArrayList<String> list1=new ArrayList<>();
list1.add("apple");
list1.add("banana");
list1.add("watermelon");
ArrayList<String> list2=new ArrayList<>();
list2.add("pig");
list2.add("cat");
list2.add("dog");
//将ArrayList类型的元素添加到list中
list.add(list1);
list.add(list2);
System.out.println(list);
}
泛型擦除
Generic<Integer> g1=new Generic<Integer>();//Generic g1=new Generic();
Generic<String> g2=new Generic<>();//Generic g2=new Generic();
Generic g3=new Generic();
//1.g1,g2,g3属于同类吗? 是同类型
//利用反射查看对象的数据类型
System.out.println(g1.getClass().getSimpleName());//Generic
System.out.println(g2.getClass().getSimpleName());//Generic
System.out.println(g3.getClass().getSimpleName());//Generic
总结:g1,g2,g3都属于Generic类型,编译完成之后,生成的class文件之后,会自动将泛型的参数去掉,运行期间g1,g2,g3都属于同类型 Generic<Integer>==> Generic ,Generic<String>==>Generic...
这个现象我们称之为泛型擦除。
泛型起到什么作用:
<1>.将检查类型提前到了编译期(如果类型不对,直接无法编译通过)
<2>.能够避免类型转换异常的风险。。。
作业:
-
将自定义MyList自己写熟练,几个基本的方法使用熟练,泛型的
public class MyList<E> { //1.定义初始化长度 private Object[] list=new Object[10]; private int size; /** * @description 将元素添加动态数组中... * @param E 新元素 * */ public void add(E ele) { //1.判断是否需要扩容 if(size==list.length) { //2.开始扩容 Object[] newArr=new Object[size+(size>>1)]; //3.将旧数组中的数据搬到新数组中 for(int i=0;i<size;i++) { newArr[i]=list[i]; } //4.新数组替换旧数组 list=newArr; } //5.将你需要添加的数据放在数组中 list[size++]=ele; } /** * @description 返回动态数组中实际的元素个数... * */ public int size() { return size; } /** * @description 根据下标获得元素的方法 * @param index 返回元素对应的下标位置 * */ public E get(int index) { if(index>=0 &&index<size) { return (E) list[index]; } //1.如果下标越界抛出异常(告诉别人哪里错了) throw new ArrayIndexOutOfBoundsException("数组越界..."); } /** * @description 根据指定的index索引位置 修改数组中的数据... * @param index 修改的索引位置 * @param ele 新元素 * */ public void set(int index,E ele) { if(index>=0&&index<size) { list[index]=ele; }else { throw new ArrayIndexOutOfBoundsException("检索的位置不存在..."); } } /** * @description 根据指定的索引位置进行删除 * @param index 删除的下标 * */ public void remove(int index) { if(index>=0&&index<size) { for(int i=index;i<size-1;i++) { //1.将后面的元素搬到前一个位置 list[i]=list[i+1]; } //2.最后一个元素置空 list[size-1]=null; size--;//没减少一个元素,长度-1 }else { throw new ArrayIndexOutOfBoundsException("检索的位置不存在..."); } } /** * @description 根据元素进行删除 * @param ele 需要删除的元素 * */ public void remove(E ele) { int index=indexOf(ele); remove(index); } /** * @description 根据指定元素返回其在数组中第一次出现的索引位置,不存在返回-1 * @param ele 需要检索位置的元素 **/ public int indexOf(E ele) { for(int i=0;i<size;i++) { if(ele.equals(list[i])){ return i; } } return -1; } /** * @description 根据指定元素返回其在数组中最后一次出现的索引位置,不存在返回-1 * @param ele 需要检索位置的元素 **/ public int lastIndexOf(E ele) { for(int i=size-1;i>=0;i--) { if(ele.equals(list[i])){ return i; } } return -1; } /** * @description 根据指定元素判断其是否存在 存在 返回true 不存在返回false * @param ele 需要检索位置的元素 * */ public boolean contains(E ele) { for(int i=0;i<size;i++) { if(ele.equals(list[i])){ return true; } } return false; } /** * @description 根据指定下标交换集合中元素的位置 * @param i,j 交换的下标 * */ public void swap(int i,int j) { if(i!=j&&i>=0&&i<size&&j>=0&&j<size) { Object temp=list[i]; list[i]=list[j]; list[j]=temp; }else { throw new ArrayIndexOutOfBoundsException("检索的位置不存在..."); } } /** * @description 改写toString()方法 方便测试数据... * */ @Override public String toString() { Object[] objs=new Object[size]; for(int i=0;i<size;i++) { objs[i]=list[i]; } return Arrays.toString(objs); } }