转载:
https://blog.csdn.net/harvic880925/article/details/49872903
https://www.bilibili.com/video/BV1eF411h7A5?p=3&share_medium=android&share_plat=android&share_session_id=0933dced-f38b-4439-8e49-f7e4c236828a&share_source=WEIXIN&share_tag=s_i×tamp=1641741952&unique_k=jJ7NQeb
java中ArrayList就是一个泛型。泛型使用的引用类型,所以在定义泛型类型的时候不能为基本类型,如List 等,只能为List ,编译器在编译的时候会进行类型擦除。
泛型类型定义的字母,任意一个大写字母都可以。他们的意义是完全相同的,但为了提高可读性,大家还是用有意义的字母比较好,一般来讲,在不同的情境下使用的字母意义如下:
E — Element,常用在java Collection里,如:List,Iterator,Set
K,V — Key,Value,代表Map的键值对
N — Number,数字
T — Type,类型,如String,Integer等等
如果这些还不够用,那就自己随便取吧,反正26个英文字母呢。
再重复一遍,使用哪个字母是没有特定意义的!只是为了提高可读性!!!!
使用泛型的好处
上一节只是讲解了泛型的引入,那么泛型带来了哪些好处呢?
- 将运行时期的ClassCastException,转移到了编译时期变成了编译失败。
- 避免了类型强转的麻烦。
一.泛型类和泛型方法
泛型,用来灵活地将数据类型应用到不同的类、方法、接口当中。将数据类型作为参数进行传递。
定义和使用泛型的类
定义格式:
修饰符 class 类名<代表泛型的变量> { }
例如,API中的ArrayList集合:
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
....
}
使用泛型类
使用的时候必须要声明使用的泛型的具体类型,如下
List list = ……,Integer str = ……,String str=……;
List<String> list = new ArrayList<>();
list.add("qqq");
String str =list.get(0);
List<Integer> list = new ArrayList<>();
list.add(0);
Integer str = list.get(0)
使用泛型: 即什么时候确定泛型。
在创建对象的时候确定泛型
例如,ArrayList<String> list = new ArrayList<String>();
ArrayList 表示定义的是个ArrayList 类型的具体类型为String,add(E e) ,类型E 和ArrayList 类型一致,表示是String类型;在定义类变量类型的时候就已经定义好了
示例二 定义泛型类
public class GenericsClassDto<T> {
private T t;
public void setParam(T t){
System.out.println("GenericsClassDto.getParam:"+t);
}
public T getParam(T t){
System.out.println("GenericsClassDto.getParam:"+t);
return t;
}
}
使用泛型类
泛型的方法
泛型方法可以优化泛型类在使用到时候声明的啰嗦的问题;省略先定义泛型类变量的问题;
定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
public class MyGenericMethod {
public <MVP> void show(MVP mvp) {
System.out.println(mvp.getClass());
}
public <MVP> MVP show2(MVP mvp) {
return mvp;
}
}
表示 修饰符泛型的变量类型 ,有了代表泛型的变量 ,就不要在类名后面进行声明变量了,这也是和泛型类的区别之一
使用格式:调用方法时,确定泛型的类型
public class GenericMethodDemo {
public static void main(String[] args) {
// 创建对象,
MyGenericMethod mm = new MyGenericMethod();
// 演示看方法提示
mm.show("aaa");
mm.show(123);
mm.show(12.45);
//编译器会自动推断出类型为double类型
var tt= mm.show(12.45);
}
}
示例二
定义泛型方法
public class GenericsMethodDto {
public<T,M> T setParam(T t, M m){
System.out.println("GenericsClassDto.getParam t:"+t);
System.out.println("GenericsClassDto.getParam k:"+m);
return t;
}
public<T,V,K> List getParam(T t, V v){
List list = new ArrayList();
System.out.println("GenericsClassDto.getParam t:"+t);
System.out.println("GenericsClassDto.getParam v:"+v);
if(t instanceof String){
list.add(t);
}
if(t instanceof Integer){
list.add(v);
}
return list;
}
}
使用泛型方法
泛型类和泛型方法的区别
定义上的区别
泛型类,在定义的时候需要在类上声明方法中含有的 泛型类变量
如: 修饰符 class 类名<代表泛型的变量>
class ArrayList<E>{
public boolean add(E e){ }
public E get(int index){ }
....
}
泛型方法
修饰符 <代表泛型的变量> 返回值类型 方法名(参数){ }
<代表泛型的变量> 有了这块的泛型类型声明就不需要在类名后再此声明泛型变量
https://www.bilibili.com/video/BV1eF411h7A5?p=3&share_medium=android&share_plat=android&share_session_id=0933dced-f38b-4439-8e49-f7e4c236828a&share_source=WEIXIN&share_tag=s_i×tamp=1641741952&unique_k=jJ7NQeb
泛型类和泛型方法在使用上的区别
泛型类必须要在使用时候定义好泛型类
使用的时候必须要声明使用的泛型的具体类型,如下
List list = ……,Integer str = ……,String str=……;
泛型方法
可以不需要在类型定义时定义这些
泛型的接口
修饰符 interface接口名<代表泛型的变量> { }
定义格式:
修饰符 interface接口名<代表泛型的变量> { }
例如,
public interface MyGenericInterface<E>{
public abstract void add(E e);
public abstract E getE();
}
使用格式:
1、定义类时确定泛型的类型
例如
public class MyImp1 implements MyGenericInterface<String> {
@Override
public void add(String e) {
// 省略...
}
@Override
public String getE() {
return null;
}
}
此时,泛型E的值就是String类型。
泛型类,泛型方法,泛型接口总结
<T> 加在哪个位置就是哪种类型,加载泛型接口上就是泛型接口,加载方法返回值前就是泛型方法,加载类名后就是泛型类;
示例二
定义泛型接口
/**
* <T>加在哪里就是哪种类型
* 加载泛型接口类上就是泛型接口类
* 加载方法返回值前就是泛型方法
* @param <T>
*/
public interface GenericsInterface<T> {
public void setParam(T t);
/**
* 泛型方法
* @param m
* @param <M>
* @return
*/
public<M> M getParam(M m);
}
泛型接口的实现类
调用泛型接口
在实现的时候也需要在泛型接口类后面指定具体的类型;
2、始终不确定泛型的类型,直到创建对象时,确定泛型的类型
例如
public class MyImp2<E> implements MyGenericInterface<E> {
@Override
public void add(E e) {
// 省略...
}
@Override
public E getE() {
return null;
}
}
确定泛型:
/*
* 使用
*/
public class GenericInterface {
public static void main(String[] args) {
MyImp2<String> my = new MyImp2<String>();
my.add("aa");
}
}
泛型的使用一
1、泛型类定义及使用
//定义
class Point<T>{// 此处可以随便写标识符号
//定义泛型属性
private T x ;
private T y ;
//定义泛型方法
public void setX(T x){//作为参数
this.x = x ;
}
public void setY(T y){
this.y = y ;
}
public T getX(){//作为返回值
return this.x ;
}
public T getY(){
return this.y ;
}
};
泛型类的确定
泛型的类型的确定在创建类的时候确定,无论是泛型方法还是泛型属性,在泛型类的时候都需要在泛型类的类名后添加
//IntegerPoint使用
Point<Integer> p = new Point<Integer>() ;
p.setX(new Integer(100)) ;
System.out.println(p.getX());
//FloatPoint使用
//创建泛型类
Point<Float> p = new Point<Float>() ;
//调用泛型类的方法
p.setX(new Float(100.12f)) ;
System.out.println(p.getX());
泛型的使用案例2
@Data
public class Persion<T, E, Y,M> {
private T message;
private Y area;
public void save(E filter) {
System.out.println(message);
System.out.println(area);
System.out.println(filter);
}
public M query(E filter) {
return (M) filter;
}
}
泛型的使用
public class GenericTest {
public static void main(String[] args) {
Persion<String,Integer,String,String> persion = new Persion<>();
persion.setArea("陕西");
persion.setMessage("商务办公");
persion.save(1);
//创建了泛型类,泛型的入参已经确定了
//persion.save("aa");//报错
String stringResult =persion.query(2);
//在创建类型的时候,泛型类的泛型参数,泛型返回值都已经去确定,因此报错
// Integer intergetResult =persion.query(2); //报错
Persion<String,String,String,Integer> man = new Persion<>();
man.save("aa");//报错
Integer intergetResult =man.query("2"); //报错
System.out.println( persion.query(2));
}
}
泛型方法的使用三
有时候泛型类和泛型方法中含有泛型的参数和返回值,类,也可以不定义为泛型
public class GenericTest {
//有返回值
public <T,M> M StaticMethod(T a){
System.out.println("harvic StaticMethod: "+a.toString());
return (M) a;
}
//无返回值
public <T> void OtherMethod(T a){
System.out.println("harvicOtherMethod: "+a.toString());
}
public static void main(String[] args) {
GenericTest genericTest = new GenericTest();
genericTest.OtherMethod("123");
}
}
在方法修饰符后定义泛型的参数和返回类型public <T,M>,这个样就不用在类名后跟泛型标识<T,M>
二.泛型接口和泛型方法
方式一
1.定义 泛型接口
public interface CorPorration<T,R> {
public R queryCorPorrationNum(T filter);
}
2.定义泛型接口的实现类
public class CorPorrationImpl implements CorPorration<String,Integer>{
@Override
public Integer queryCorPorrationNum(String filter) {
return 5;
}
}```
3.调用定义好的泛型接口
```java
CorPorration<String,Integer> corPorration = new CorPorrationImpl();
Integer num= corPorration.queryCorPorrationNum("1233");
泛型的类型确定在,实现类中已经定义好了
方式二
1.定义泛型接口
public interface CorPorration<T,R> {
public R queryCorPorrationNum(T filter);
}
2、定义泛型的实现类
public class CorPorrationTwoImpl<T,R> implements CorPorration<T,R>{
@Override
public R queryCorPorrationNum(T filter) {
return (R) filter;
}
}
泛型类型的实现如果还是泛型,则要在类名称后跟上<T,R>
三、泛型类的创建
CorPorration<String,Integer> corPorrationTwo = new CorPorrationTwoImpl();
Integer numTwo= corPorrationTwo.queryCorPorrationNum("1233");
泛型类型的确定在创建类的时候
泛型类型返回值
上面我们的函数中,返回值都是void,但现实中不可能都是void,有时,我们需要将泛型变量返回,比如下面这个函数:
静态方法中也可以含有泛型如下,静态类型中含有的泛型da
public class GenericTest {
public static <T> List<T> parseArray(String response,Class<T> object){
List<T> modelList = JSON.parseArray(response, object);
return modelList;
}
函数返回值是List类型。至于传入参数Class object的意义,我们下面会讲。这里也就是想通过这个例子来告诉大家,泛型变量其实跟String,Integer,Double等等的类的使用上没有任何区别,T只是一个符号,可以代表String,Integer,Double……这些类的符号,在泛型函数使用时,直接把T看到String,Integer,Double……中的任一个来写代码就可以了。唯一不同的是,要在函数定义的中在返回值前加上标识泛型;
静态方法中的类型和参数可以是泛型,在定义类的时候不需要是在类名称后跟上泛型参数;
其它用法:Class类传递及泛型数组
有时候需要传递Class类的泛型,示例如下
首先,我们应该把SuccessModel单独抽出来做为泛型变量,但parseArray()中用到的SuccessModel.class要怎么弄呢?
先来看代码
public static List<SuccessModel> parseArray(String response){
List<SuccessModel> modelList = JSON.parseArray(response, SuccessModel.class);
return modelList;
使用泛型后如下,传递Class 类型的 class 泛型如下
public static <T> List<T> parseArray(String response,Class<T> object){
List<T> modelList = JSON.parseArray(response, object);
return modelList;
}
泛型类、泛型接口整合类
模仿List 泛型接口实现
泛型接口定义
泛型接口的实现
接口调用
泛型数组
在写程序时,大家可能会遇到类似String[] list = new String[8];的需求,这里可以定义String数组,当然我们也可以定义泛型数组,泛型数组的定义方法为 T[],与String[]是一致的,下面看看用法:
//定义
public static <T> T[] fun1(T...arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
//使用
public static void main(String args[]){
Integer i[] = fun1(1,2,3,4,5,6) ;
Integer[] result = fun1(i) ;
}
我们先看看 定义时的代码:
public static <T> T[] fun1(T...arg){ // 接收可变参数
return arg ; // 返回泛型数组
}
首先,定义了一个静态函数,然后定义返回值为T[],参数为接收的T类型的可变长参数。如果有同学对T…arg的用法不了解,可以去找下JAVA 可变长参数方面的知识。
由于可变长参数在输入后,会保存在arg这个数组中,所以,我们直接把数组返回即可。
三.泛型的通配符
当使用类或者泛型接口的时候,传递的参数中,泛型的类型不确定可以通过通配符<?>表示。但是一旦使用通配符后,只能使用Objecct类的共性方法,集合中元素自身的方法无法使用;
?代表任意待待确定的类型,只能使用Object的方法,同时List<?> 定义变量只能遍历,不能添加和修改;许多源码中也这样使用,告诉读者集合不存在方法内修改和添加;
![在这里插入图片描述](https://img-blog.csdnimg.cn/cef6c504a7404942a52ae7ac2fba3f75.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5oeS6bif5LiA5p6a,size_20,color_FFFFFF,t_70,g_se,x_16
通配符的基本使用
泛型的通配符不知道用什么类型来接受的时候,此时可以使用?表示未知通配符;此时只能接受数据不能往该集合中存储数据。
ist<?> , ?通配符定义的变量只能遍历不能修改
泛型擦除机制
所谓的泛型擦除机制是指
将 一个List 集合赋值给一个没有使用到泛型集合,直接使用泛型擦除机制
泛型是在编译阶段限制类型传递的,在运行阶段都是擦除底层class 文件在运行里面是没有泛型