20、Java 泛型
1、晓之以理,动之以码
学Java就是很上头哦,一来直接三连问!!!
什么是泛型?为什么要用泛型?泛型怎么用?
当然泛型在Java中有很重要的一个地位,在面向对象编程以及在各种设计模式中有非常广泛的应用~
1.1 什么是泛型?
泛型:
本意就是“参数化类型”。就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
1.2 一个栗子
一个被举烂的例子
package com.example.main; /**
* @author lin
* @version 1.0
* @date 2020/7/12 21:31
* @Description TODO
*/
public class TestTypeMain {
private static int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
} private static float add(float a, float b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
} private static double add(double a, double b) {
System.out.println(a + "+" + b + "=" + (a + b));
return a + b;
} private static <T extends Number> double add(T a, T b) {
System.out.println(a + "+" + b + "=" + (a.doubleValue() + b.doubleValue()));
return a.doubleValue() + b.doubleValue();
} public static void main(String[] args) {
System.out.println(TestTypeMain.add(1, 2));
System.out.println(TestTypeMain.add(1f, 2f));
System.out.println(TestTypeMain.add(1d, 2d));
System.out.println(TestTypeMain.add(Integer.valueOf(1), Integer.valueOf(2)));
System.out.println(TestTypeMain.add(Float.valueOf(1), Float.valueOf(2)));
System.out.println(TestTypeMain.add(Double.valueOf(1), Double.valueOf(2)));
} }
运行结果:
1+2=3
3
1.0+2.0=3.0
3.0
1.0+2.0=3.0
3.0
1+2=3.0
3.0
1.0+2.0=3.0
3.0
1.0+2.0=3.0
3.0
1.3 总结(意义):
1:减少代码,增强可读性,代码复用性更高
2:显性指定类型,编译器会自动检查,避免运行中的一半的错误。
2、泛型如何使用?
2.1 泛型可以用到哪里?
1、泛型方法:public static < E > void printArray( E[] inputArray )
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
2、泛型类:public class Box<T> { private T t; }
3、泛型接口:public interface Generator<T> { public T next(); }
4、泛型通配符:List<String> list= new ArrayList<String>();
2.2 泛型类型的变量
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)
2.3 泛型方法
简单的泛型方法入门可以参考上面的那个栗子. 另外通过一个例子,把泛型方法再总结一下。
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//当然这个例子举的不太合适,只是为了说明泛型方法的特性。
T test = container.getKey();
return test;
} //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
} //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
} /**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
public <T> T showKeyName(Generic<E> container){
...
}
*/ /**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
public void showkey(T genericObj){ }
*/
此外:
/**
* 这才是一个真正的泛型方法。
* 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
* 这个T可以出现在这个泛型方法的任意位置.
* 泛型的数量也可以为任意多个
* 如:public <T,K> K showKeyName(Generic<T> container){
* ...
* }
*/
public <T> T showKeyName(Generic<T> container){
System.out.println("container key :" + container.getKey());
//当然这个例子举的不太合适,只是为了说明泛型方法的特性。
T test = container.getKey();
return test;
} //这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
public void showKeyValue1(Generic<Number> obj){
Log.d("泛型测试","key value is " + obj.getKey());
} //这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
//同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
public void showKeyValue2(Generic<?> obj){
Log.d("泛型测试","key value is " + obj.getKey());
} /**
* 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
* 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
* 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
public <T> T showKeyName(Generic<E> container){
...
}
*/ /**
* 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
* 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
* 所以这也不是一个正确的泛型方法声明。
public void showkey(T genericObj){ }
*/
2.4 泛型类
定义一个泛型类:public class GenericClass
{}
package com.example.main; /**
* @author lin
* @version 1.0
* @date 2020/7/12 21:49
* @Description 泛型类
*/
public class GenericClass<T> {
private T data; public T getData() {
return data;
} public void setData(T data) {
this.data = data;
} public static void main(String[] args) {
GenericClass<String> genericClass = new GenericClass<>();
genericClass.setData("Generic Class");
System.out.println(genericClass.getData());
}
}
2.5 泛型接口
定义一个泛型接口:public interface GenericIntercace
{}
package com.example.main; /**
* @author lin
* @version 1.0
* @date 2020/7/12 21:50
* @Description TODO
*/
public interface GenericInterface<T> {
public T getData();
}
实现泛型接口不指定类型方式一:
public class ImplGenericInterface1
implements GenericIntercace
package com.example.main; /**
* @author lin
* @version 1.0
* @date 2020/7/12 21:53
* @Description TODO
*/
public class ImplGenericInterface1<T> implements GenericInterface<T> {
private T data; private void setData(T data) {
this.data = data;
} @Override
public T getData() {
return this.data;
} public static void main(String[] args) {
ImplGenericInterface1<String> implGenericInterface1 = new ImplGenericInterface1<>();
implGenericInterface1.setData("Generic Interface1");
System.out.println(implGenericInterface1.getData());
}
}
实现泛型接口指定类型方式二:
public class ImplGenericInterface2 implements GenericIntercace<String>
{}
package com.example.main; /**
* @author lin
* @version 1.0
* @date 2020/7/12 21:55
* @Description TODO
*/
public class ImplGenericInterface2 implements GenericInterface<String> {
@Override
public String getData() {
return "hello world ";
} public static void main(String[] args) {
ImplGenericInterface2 implGenericInterface2 = new ImplGenericInterface2();
System.out.println(implGenericInterface2.getData());
}
}
2.6 泛型通配符
引入通配符可以在泛型实例化时更加灵活地控制,也可以在方法中控制方法的参数。
语法如下:
泛型类名<? extends T> 或 泛型类名<? super T> 或 泛型类名<?>? extends T:表示T或T的子类
? super T:表示T或T的父类
?:表示可以是任意类型
常用的 T,E,K,V,?
本质上这些个都是通配符,没啥区别,只不过是编码时的一种约定俗成的东西。比如上述代码中的 T ,我们可以换成 A-Z 之间的任何一个 字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替 T ,在可读性上可能会弱一些。通常情况下,T,E,K,V,?是这样约定的:
2.6.1、?表示不确定的 java 类型
static int countLegs (List<? extends Animal > animals ) {
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
} static int countLegs1 (List< Animal > animals ){
int retVal = 0;
for ( Animal animal : animals )
{
retVal += animal.countLegs();
}
return retVal;
}
2.6.2、T (type) 表示具体的一个java类型
下界: 用 super 进行声明,表示参数化的类型可能是所指定的类型,或者是此类型的父类型,直至 Object
在类型参数中使用 super 表示这个泛型中的参数必须是 E 或者 E 的父类。 private <T> void test(List<? super T> dst, List<T> src){
for (T t : src) {
dst.add(t);
}
} public static void main(String[] args) {
List<Dog> dogs = new ArrayList<>();
List<Animal> animals = new ArrayList<>();
new Test3().test(animals,dogs);
}
实例:
class gent<T> {
public void test() {
System.out.println("gent");
}
} class supC {
public String toString() {
return "supA";
}
} public class Bc extends supC {
String b; public Bc( String b ) {
this.b = b;
} public String toString() {
return "subB";
} // 指明泛型参数必须是supC或其子类
public void test( gent<? extends supC> o ) {
System.out.println("Bc");
} public static void main( String[] args ) {
Bc bc = new Bc("test");
gent<Bc> oGent = new gent<Bc>();
bc.test(oGent); // oGent 是supC的子类对象
}
}
3、泛型注意点
3.1 静态方法与泛型
静态方法有一种情况需要注意一下,那就是在类中的静态方法使用泛型:静态方法无法访问类上定义的泛型;如果静态方法操作的引用数据类型不确定的时候,必须要将泛型定义在方法上。
即:如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法 。
public class StaticGenerator<T> {
....
....
/**
* 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
* 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
* 如:public static void show(T t){..},此时编译器会提示错误信息:
"StaticGenerator cannot be refrenced from static context"
*/
public static <T> void show(T t){ }
}
3.2 泛型类型继承规则
1,对于泛型参数是继承关系的泛型类之间是没有继承关系的 2,泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E> 3,泛型类的继承关系在使用中同样会受到泛型类型的影响
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型继承规则测试类
*/
public class GenericInherit<T> {
private T data1;
private T data2; public T getData1() {
return data1;
} public void setData1(T data1) {
this.data1 = data1;
} public T getData2() {
return data2;
} public void setData2(T data2) {
this.data2 = data2;
} public static <V> void setData2(GenericInherit<Father> data2) { } public static void main(String[] args) {
// Son 继承自 Father
Father father = new Father();
Son son = new Son();
GenericInherit<Father> fatherGenericInherit = new GenericInherit<>();
GenericInherit<Son> sonGenericInherit = new GenericInherit<>();
SubGenericInherit<Father> fatherSubGenericInherit = new SubGenericInherit<>();
SubGenericInherit<Son> sonSubGenericInherit = new SubGenericInherit<>(); /**
* 对于传递的泛型类型是继承关系的泛型类之间是没有继承关系的
* GenericInherit<Father> 与GenericInherit<Son> 没有继承关系
* Incompatible types.
*/
father = new Son();
// fatherGenericInherit=new GenericInherit<Son>(); /**
* 泛型类可以继承其它泛型类,例如: public class ArrayList<E> extends AbstractList<E>
*/
fatherGenericInherit=new SubGenericInherit<Father>(); /**
*泛型类的继承关系在使用中同样会受到泛型类型的影响
*/
setData2(fatherGenericInherit);
// setData2(sonGenericInherit);
setData2(fatherSubGenericInherit);
// setData2(sonSubGenericInherit); } private static class SubGenericInherit<T> extends GenericInherit<T> { }
3.3 虚拟机是如何实现泛型的
Java泛型是Java1.5之后才引入的,为了向下兼容。Java采用了C++完全不同的实现思想。Java中的泛型更多的看起来像是编译期用的 Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object.
GenericClass<String> stringGenericClass=new GenericClass<>();
GenericClass<Integer> integerGenericClass=new GenericClass<>();
C++中GenericClass<String>和GenericClass<Integer>是两个不同的类型 Java进行了类型擦除之后统一改为GenericClass<Object>
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description:泛型原理测试类
*/
public class GenericTheory {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("Key", "Value");
System.out.println(map.get("Key"));
GenericClass<String, String> genericClass = new GenericClass<>();
genericClass.put("Key", "Value");
System.out.println(genericClass.get("Key"));
} public static class GenericClass<K, V> {
private K key;
private V value; public void put(K key, V value) {
this.key = key;
this.value = value;
} public V get(V key) {
return value;
}
} /**
* 类型擦除后GenericClass2<Object>
* @param <T>
*/
private class GenericClass2<T> { } /**
* 类型擦除后GenericClass3<ArrayList>
* 当使用到Serializable时会将相应代码强制转换为Serializable
* @param <T>
*/
private class GenericClass3<T extends ArrayList & Serializable> { }
}
对应的字节码文件
public static void main(String[] args) {
Map<String, String> map = new HashMap();
map.put("Key", "Value");
System.out.println((String)map.get("Key"));
GenericTheory.GenericClass<String, String> genericClass = new GenericTheory.GenericClass();
genericClass.put("Key", "Value");
System.out.println((String)genericClass.get("Key"));
}
4.学以致用
1,泛型解析JSON数据封装
api返回的json数据
{
"code":200,
"msg":"成功",
"data":{
"name":"Lin",
"email":"10086"
}
}
BaseResponse .java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 接口数据接收基类
*/
public class BaseResponse { private int code;
private String msg; public int getCode() {
return code;
} public void setCode(int code) {
this.code = code;
} public String getMsg() {
return msg;
} public void setMsg(String msg) {
this.msg = msg;
}
}
UserResponse.java
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 用户信息接口实体类
*/
public class UserResponse<T> extends BaseResponse {
private T data; public T getData() {
return data;
} public void setData(T data) {
this.data = data;
}
}
2,泛型+反射实现巧复用工具类
/**
* Author:lin 2020/7/12 21:55
* <p>
* Description: 泛型相关的工具类
*/
public class GenericUtils { public static class Movie {
private String name;
private Date time; public String getName() {
return name;
} public Date getTime() {
return time;
} public Movie(String name, Date time) {
this.name = name;
this.time = time;
} @Override
public String toString() {
return "Movie{" + "name='" + name + '\'' + ", time=" + time + '}';
}
} public static void main(String[] args) {
List<Movie> movieList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
movieList.add(new Movie("movie" + i, new Date()));
}
System.out.println("排序前:" + movieList.toString()); GenericUtils.sortAnyList(movieList, "name", true);
System.out.println("按name正序排:" + movieList.toString()); GenericUtils.sortAnyList(movieList, "name", false);
System.out.println("按name逆序排:" + movieList.toString());
} /**
* 对任意集合的排序方法
* @param targetList 要排序的实体类List集合
* @param sortField 排序字段
* @param sortMode true正序,false逆序
*/
public static <T> void sortAnyList(List<T> targetList, final String sortField, final boolean sortMode) {
if (targetList == null || targetList.size() < 2 || sortField == null || sortField.length() == 0) {
return;
}
Collections.sort(targetList, new Comparator<Object>() {
@Override
public int compare(Object obj1, Object obj2) {
int retVal = 0;
try {
// 获取getXxx()方法名称
String methodStr = "get" + sortField.substring(0, 1).toUpperCase() + sortField.substring(1);
Method method1 = ((T) obj1).getClass().getMethod(methodStr, null);
Method method2 = ((T) obj2).getClass().getMethod(methodStr, null);
if (sortMode) {
retVal = method1.invoke(((T) obj1), null).toString().compareTo(method2.invoke(((T) obj2), null).toString());
} else {
retVal = method2.invoke(((T) obj2), null).toString().compareTo(method1.invoke(((T) obj1), null).toString());
}
} catch (Exception e) {
System.out.println("List<" + ((T) obj1).getClass().getName() + ">排序异常!");
e.printStackTrace();
}
return retVal;
}
});
}
}
3,Gson库中的泛型的使用-TypeToken
/**
* Author:Jay On 2019/5/11 22:11
* <p>
* Description: Gson库中的泛型使用
*/
public class GsonGeneric {
public static class Person {
private String name;
private int age; public Person(String name, int age) {
this.name = name;
this.age = age;
} @Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
} public static void main(String[] args) {
Gson gson = new Gson();
List<Person> personList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
personList.add(new Person("name" + i, 18 + i));
}
// Serialization
String json = gson.toJson(personList);
System.out.println(json);
// Deserialization
Type personType = new TypeToken<List<Person>>() {}.getType();
List<Person> personList2 = gson.fromJson(json, personType);
System.out.println(personList2);
}
}
发哥讲
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注公众号
20、Java 泛型的更多相关文章
- 浅析Java 泛型
泛型是JavaSE5引入的一个新概念,但是这个概念在编程语言中却是很普遍的一个概念.下面,根据以下内容,我们总结下在Java中使用泛型. 泛型使用的意义 什么是泛型 泛型类 泛型方法 泛型接口 泛型擦 ...
- 18.Java泛型
1.为什么需要泛型 List list1=new ArrayList(Arrays.asList(new String("string"),new Integer(20))); S ...
- 【Java心得总结四】Java泛型下——万恶的擦除
一.万恶的擦除 我在自己总结的[Java心得总结三]Java泛型上——初识泛型这篇博文中提到了Java中对泛型擦除的问题,考虑下面代码: import java.util.*; public clas ...
- Java泛型学习笔记 - (七)浅析泛型中通配符的使用
一.基本概念:在学习Java泛型的过程中, 通配符是较难理解的一部分. 主要有以下三类:1. 无边界的通配符(Unbounded Wildcards), 就是<?>, 比如List< ...
- 全面总结Java泛型
本文对Java泛型进行了全面的总结.文章内容包括普通泛型.通配符.受限泛型.泛型接口.泛型方法.返回泛型类型实例等等. 虽然Scala创始人Martin Odersky说当年正是因为Java泛型的丑陋 ...
- Java学习笔记(二一)——Java 泛型
[前面的话] 最近脸好干,掉皮,需要买点化妆品了. Java泛型好好学习一下. [定义] 一.泛型的定义主要有以下两种: 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个 ...
- Java 泛型(Generics)
Generics, 类似C++中的模版. 允许在定义类和接口的时候使用类型参数(type parameters), 声明的类型参数在使用的时候用具体的类型来替换. 如 ArrayList<Str ...
- java泛型 8 泛型的内部原理:类型擦除以及类型擦除带来的问题
参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
- Java泛型介绍!!!
Java总结篇系列:Java泛型 转自:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 一. 泛型概念的提出(为什么需要泛型)? 首先,我们看下下 ...
随机推荐
- EM算法的收敛性
https://blog.csdn.net/kevinoop/article/details/80522477
- echarts 实战 : 恼人的间隔问题
使用 echarts 的时候,可能我们需要这个图表的间隔是固定的.比如 3个 4个 5个. (注意计算间隔数量的时候是不算 x轴 本身的.) 这个问题看似简单,其实有点麻烦. yAxis.splitN ...
- SW算法求全局最小割(Stoer-Wagner算法)
我找到的唯一能看懂的题解:[ZZ]最小割集Stoer-Wagner算法 似乎是一个冷门算法,连oi-wiki上都没有,不过洛谷上竟然有它的模板题,并且2017百度之星的资格赛还考到了.于是来学习一下. ...
- CentOS7编译安装php7.1配置教程详解
这篇文章主要介绍CentOS7编译安装php7.1的过程和配置详解,亲测 ,需要的朋友可以参考. 1.首先安装依赖包: yum install libxml2 libxml2-devel openss ...
- CODING DevOps + Nginx-ingress 实现自动化灰度发布
作者:王炜,CODING DevOps 后端开发工程师,拥有多年研发经验,云原生.DevOps.Kubernetes 资深爱好者,Servicemesher 服务网格中文社区成员.获得 Kuberne ...
- APP自动化 -- 获取toast元素的文本内容
一.toast元素 1.表现形式:toast元素就是下图中 “操作成功” 那个一闪而过的标签. 2.特殊点:因为一闪而过,时间太短,用UIAutomatorView截屏截不到. 二.获取方法 1.用 ...
- 16 . Go之网络编程
互联网的本质 两台计算机之间的通信与两个人打电话原理是一样的. # 1. 首先要通过各种物理连接介质连接 # 2. 找准确对方计算机(准确到软件)的位置 # 3. 通过统一的标准(一般子协议)进行数据 ...
- sqlite 显示表内容时乱码,无法正常显示汉字,
把txt文件另存为时,选择编码为utf-8即可
- 初学Vue.js,用 vue ui 创建项目会不会被鄙视
全栈的自我修养: 6使用vue ui进行vue.js环境搭建 It is only with the heart that one can see rightly. What is essential ...
- 使用MacOS自带的SVN客户端
原文链接:https://jingyan.baidu.com/article/5552ef479c1554518ffbc92f.html 摘要:mac环境下有自带的SVN服务端和客户端,SVN是许多公 ...