带大家认识Java语法之泛型与通配符
285
2022-08-06
Java 深入浅出讲解泛型与包装类(java史莱姆娘模组)
目录1、什么是泛型2、泛型的语法3、泛型的上界4、通配符(1)通配符的上界(2)通配符的下界5、包装类
1、什么是泛型
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。
先看以下的例子:
我们以前学过的数组,只能存放指定类型的元素。如:int[] array=new int[10];String[] array=new String[10];而Object类是所有类的父类,那么我们是否可以创建Obj数组呢?
class Myarray{
public Object[] array=new Object[10];
public void setVal(int pos,Object val){
this.array[pos]=val;
}
public Object getPos(int pos){
return this.array[pos];
}
}
public class TestDemo{
public static void main(String[] args) {
Myarray myarray=new Myarray();
myarray.setVal(1,0);
gmfJZu myarray.setVal(2,"shduie");//字符串也可以存放
String ret=(String)myarray.getPos(2);//虽然我们知道它是字符串类型,但是还是要强制类型转换
System.out.println(ret);
}
}
以上代码实现后,我们发现:
任何类型的数据都能存放2号下标本来就是字符串,但是必须进行强制类型转换
以此引出泛型,泛型的目的就是:指定当前的容器要持有什么类型的对象,让编译器自己去检查。
2、泛型的语法
class 泛型类名称< 类型形参列表>{
//这里可以使用类型参数
}
泛型的使用:
泛型类<类型实参> 变量名=new 泛型类<类型实参>(构造方法实参)
MyArray list=new MyArray<>();
【注】
类型后的<>代表占位符,表示当前类是一个泛型类在实例化泛型时,<>中不能是简单的类型,需要是包装类<>不参与泛型的类型组成不能new泛型类型的数组使用泛型不需要进行强制类型转换
一个简单的泛型:
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Test
//key这个成员变量的类型为T,T的类型由外部指定
private T key;
public Test(T key) { //泛型构造方法形参key的类型也为T,T的类型由外部指定
this.key = key;
}
public T getKey(){ //泛型方法getKey的返回值类型为T,T的类型由外部指定
return key;
}
}
擦除机制:编译时会将<>中的类型擦除掉,所以<>中的东西不参与类型的组成。会将T擦除为Object。
为什么不能实例化泛型类型的数组?
数组和泛型之间的一个重要区别是它们如何强制执行类型检查。数组在运行时存储和检查类型信息,而泛型是在编译时检查类型错误。
返回的Object数组里面,可能存放着任何类型的数据,如string,通过int类型的数组来接收,编译器认为是不安全的。
3、泛型的上界
语法:
class 泛型类名称
}
例:
public class MyArray{} //E只能是Number或Number的子类
public class MyArray
//E一定实现了Comparable接口的类
【注】没有指定边界的E,可以看作 E extends Object
4、通配符
? 用于在泛型的使用,即为通配符。通配符用来解决反泛型无法协变的问题。
如下两段代码:
代码一:
public static
for(T x:list){
System.out.println(x);
}
}
代码二:
public static
for(Object x:list){
System.out.println(x);
}
}
代码2中使用了通配符,和代码1相比,此时传入代码1的具体是什么数据类型,我们是不清楚的。
(1)通配符的上界
语法:
extends 上界>
extends Number>//可以传入的实参类型为Number或Number的子类
例:对于以下关系,我们需要写一个方法来打印存储了Animal或者Animal子类的list。
AnimalCat extends AnimalDog extends Animal
代码一:
public static
for(T animal:list){
System.out.println(animal);//调用了T的toString
}
}
此时T类型是Animal的子类或自己。
代码二:通过通配符实现
public static void print2(List extends Animal> list){
for(Animal animal:list){
Syatem.out.println(animal);//调用了子类的toString方法
}
}
两种代码的区别:
对于泛型实现的方法来说,
通配符上界→父子类关系:
//需要使用通配符来确定父子类型
MyArrayList extends Number>是MyArrayList
MyArrayList>是MyArrayList extends Number>的父类
ArrayList
ArrayList
List extends Number> list = arrayList1;
//list.add(1,1);//报错,此时list的引用的子类对象有很多,再添加的时候,任何子类型都可以,为了安全,java不让这样进行添加操作。
Number a = list.get(0);//可以通过
Integer i = list.get(0);//编译错误,只能确定是Number子类
【注】
不能对其进行添加,list中存储的可能是Number也可能是Number的子类,无法确定类型。通配符上界适合读取,不适合写入。
(2)通配符的下界
语法:
super 下界>
super Integer>//可以传入的参数类型是Integer或者Integer的父类
通配符下界的父子类关系:
MyArrayList super Integer>是MyArraygmfJZuList
MyArrayList<?>是MyArrayList super Integer>的父类
通配符下界适合写入元素,不适合读取。
5、包装类
在Java中,由于基本类型不是继承自Object,为了在泛型中可以支持基本类型,每个基本类型都对应了一个包装类。除了Integer和Character,其余基本类型的包装类都是首字母大写。
拆箱和装箱:
int i=10;
//装箱操作,新建一个Integer类型对象,将i的值放入对象的某个属性中
Integer ii=i; //自动装箱
//Integer ii=Integer.valueOf(i);
Integer ij= new Integer(i);//显示装箱
//拆箱操作,将Integer对象中的值取出,放到一个基本数据类型中
int j=ii.intValue();//显示的拆箱
int jj=ii;//隐式的拆箱
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~