wordpress注册未发送邮件,长春企业网站seo,阳光家园广州网站网址,深圳福田区有什么好玩的景点3.3泛型
3.3.1泛型出现的背景
泛型出现的背景有两点#xff1a;
第一点是在集合容器中#xff0c;如果没有指定对应类型的话#xff0c;那么底层的元素就是object#xff0c;要对容器中的元素进行存取的时候#xff0c;取出来的同时需要进行类型转换#xff0c;如果有…3.3泛型
3.3.1泛型出现的背景
泛型出现的背景有两点
第一点是在集合容器中如果没有指定对应类型的话那么底层的元素就是object要对容器中的元素进行存取的时候取出来的同时需要进行类型转换如果有的类型不支持强制类型转换这个时候就会报错因此泛型的出现能够在一开始的时候就指定相应的类型这就不会造成出错第二点是为了实现代码的复用比如有一个做加法的函数加法既可以做int数据的加法也可以做long数据的加法但是如果没有泛型固定写死的话那就需要去根据不同的数据类型去创建对应的方法有了泛型之后就可以在调用方法时直接指定对应的类型就可以
3.3.2泛型的基本使用
泛型类
/*** 泛型类* param T*/
class ObjT{T var;public T getObj(){return this.var;}
}
/*** 多元泛型类* param T,E*/
class MutliObjT,E{T var1;E var2;public T getVar1(){return this.var1;}public T getvar2(){return this.var1;}}泛型接口
/*** 泛型接口* param T*/
interface InfoT{/*** 在泛型接口中定义方法* param info* return*/public T getInfo(T info);
}/*** 实现泛型接口的类*/
class InfomationImpl implements InfoString{String info;public void setInfo(String info){this.info info;}Overridepublic String getInfo(String info) {return info;}
}泛型方法
/*** 定义泛型方法的类*/
class FxMethod {/*** 泛型方法* param var1* param var2* param T* param E* return*/public T,E T method(T var1, E var2){if (var2!null){System.out.println(var2);}return var1;}
}泛型的上下限 泛型的上限在做为参数的时候使用extends Father表示当前传入的参数只能是Father或者Father的子类才行
class InfoT extends Number{ // 此处泛型只能是数字类型private T var ; // 定义泛型变量public void setVar(T var){this.var var ;}public T getVar(){return this.var ;}public String toString(){ // 直接打印return this.var.toString() ;}
}
public class demo1{public static void main(String args[]){InfoInteger i1 new InfoInteger() ; // 声明Integer的泛型对象}
}泛型的下限在声明泛型的时候使用? super Son表示当前传入的参数只能是Son或者Son的父类才行
class InfoT{private T var ; // 定义泛型变量public void setVar(T var){this.var var ;}public T getVar(){return this.var ;}public String toString(){ // 直接打印return this.var.toString() ;}
}
public class GenericsDemo21{public static void main(String args[]){InfoString i1 new InfoString() ; // 声明String的泛型对象InfoObject i2 new InfoObject() ; // 声明Object的泛型对象i1.setVar(hello) ;i2.setVar(new Object()) ;fun(i1) ;fun(i2) ;}public static void fun(Info? super String temp){ // 只能接收String或Object类型的泛型String类的父类只有Object类System.out.print(temp , ) ;}
}
泛型数组常用的集合如ListSetQueue等在定义时都会指定对应的类型从而创建泛型数组
3.3.3泛型擦除 泛型其实是Java中的一个语法糖为的就是解决上面所说的问题而在编译成字节码的时候会将里面的泛型都替换为确切的类这个过程就是泛型擦除 泛型擦除包含了三种类型 第一种是没有指定上下限的在编译的时候就会将所有泛型都转换成Object类第二种是指定了上限的那么在编译的时候就会将所有泛型转换成上限这个类比如,那在编译的时候就会转换成Number类第三种是指定了下限的在编译的时候会将所有泛型转换成指定下限的父类比如? super Number编译时会替换成Object 泛型擦除会有什么问题呢 第一个问题 数据的继承性问题在Java中数据是具备继承性的比如Integer 继承 Number在数组中如果定义Object[] objArr new Object[3]; objArr[0]“abc”;objArr[1]1;这在编译的时候是没有问题的但是如果定义了ArrayList list new ArrayList();这就会报错因为在编译的时候泛型会进行擦除擦除之后的语句变成ArrayList list new ArrayList()这样虽然看起来没什么问题但是编译器对于左右两边的类型就无法判断是不是兼容。再细致点讲比如如下代码
ListObject list new ArrayList();list.add(123);list.add(abc); ListString newList new ArrayList();newList list;上面的代码中list和newList的类型是不一样的对其进行赋值相当于上面的操作如果能赋值成功那么久会导致newList中的元素既有int类型又有String类型这样是会造成错乱的所以编译器不允许这种形式的存在。 第二个问题 - 同样是因为数据具备继承性比如我创建一个方法 method(List list)我希望我传入的是List类型的时候这个方法也能调用其实这个过程就转换成上面的第一问题了我想传入子类参数实际上就是赋值操作让List list new List这显然是不可以的 - 这时候想到了由于一个是Object类型的list一个是String类型的list那么我可不可以对方法进行重载呢只要将参数设置成不同类型即可就像下面的代码所示这样
public class Cmower {public static void method(ArraylistObject list) {System.out.println(ArraylistObject list);}public static void method(ArraylistString list) {System.out.println(ArraylistString list);}}看似这样定义方法能解决上面的问题但是实际上解决不了同样是在编译的时候会进行泛型擦除上面两个形参在编译时他们的形参都会转成Arraylist list这其实就变成了同一种方法所以这种方式并不能解决上面的问题 泛型擦除可以怎么验证呢 获得两个泛型的Class对象让他们进行操作得到的结果是true
//泛型擦除ArrayListString arrlist1 new ArrayList();ArrayListInteger arrlist2 new ArrayList();System.out.println(arrlist1.getClass()arrlist2.getClass());3.3.4泛型通配符 在上面的泛型擦除问题中讲到的两个问题都导致了我们在使用泛型的时候没办法用到数据的继承性所以这个时候就出现了泛型通配符为的就是解决上面的问题 泛型通配符的使用 ? extends Father表示这时候可以赋值Father及他的子类如下面的代码 ArrayList? extends Number list new ArrayListInteger(); 我们来分析一下他为什么能解决上面的泛型擦除的问题因为指定了当前ArrayList的上限为Number所以在编译的时候就知道无论如何list中都是存放Number的子类对象的就不用担心左右两边出现类型不确定的问题如下面的代码 ? super Son表示这时候可以赋值Son及他的父类如下面的代码 ArrayList? super Integer list new ArrayListNumber();原因其实和上面extends的一样 ?只有通配符的话就表示没有限定类型 泛型通配符的使用场景 上面的做法虽然解决了泛型擦除的问题但是在实际使用中要注意使用的场景这里有两条原则就是用了? extends Father的集合不能调用add()方法用了? super Son的集合不能调用get()方法具体原因如下 ? extends Father的集合不能调用add()方法: 如果现在有一个集合ArrayList? extends Number list new ArrayList()同时允许去调用add()方法那么既可以往里面加入int也可以加入float这样再去做赋值操作的话比如list new ArrayList()就会出现问题因为new ArrayList()限定了类型只能为Integer但是list里面确可能包含不止Integer一种类型的元素。把这个过程变成下面的代码 ArrayList? extends Number list new ArrayList();
list.add(123);
list.add(2.3);
ArrayListInteger newList new ArrayList();
//newList限定了类型但是list中的元素包含不止Integer的类型
newList list;而? super Son的集合确可以调用add()方法原因是无论后面赋值的是什么类型的集合都必须是Son或者Son的父类因此就不会导致加入的元素和定义的类型不同的问题比如下面的代码 ArrayList? super Integer list new ArrayList();
list.add(123);
ArrayListNumber newList new ArrayList();
newList.add(3.14);
list newList;? super Son无法调用get()方法 因为如果可以调用get()方法的话这个list里面不仅包括Son还可能包括Son的父类元素比如下面的代码 ArrayList? super Integer list new ArrayList();
list.add(123);
ArrayListNumber newList new ArrayList();
newList.add(3.14);
list newList;由于list可以被newList赋值所以list里面并不一定只存放了Integer还可能是Number所以这是后去get()就不确定是什么类型了。 而extends Father是可以调用get()方法的原因就在于赋值的时候必须是Father的子类所以无论传入的是Father还是Father的子类其实都是Father类所以就不担心get的时候会搞不清是什么类型如下面的代码 ArrayListInteger newList2 new ArrayList();
newList2.add(123);
ArrayListDouble newList3 new ArrayList();
newList3.add(2.3);
list2newList2;
list2.get(0);
list2 newList3;
list2.get(0);