前言
第一次接触 java 的时候,一次编译,到处运行,给人的感觉就很强大,必须入坑啊。不过现在 java 主流使用场景还是后端这块,图形化界面开发并不擅长。java 在后端语言的竞争中依然屹立不倒,和 java 的生态有很大关系,但不可否认的是,java 在高并发场景下的稳定性确实可靠。今天还看了一个关于够浪(golang) 语言的评价,也准备考虑入门下作为技术储备。有时间学习的时候,我一定要多学点。
最近打算花时间重拾 java ,决定看看 jdk 源码,再提升一个层次。也想写一个关于 java 和 SpringBoot 的系列博客。
泛型
JDK 1.5 之前
当我入坑 java 的时候,那时候还是 jdk 1.7 的天下。不过泛型确实是个好东西。没有泛型之前我们可能会这样写代码
@Test
public void run1(){
// 有个集合你想保存 String 类型数据
List list =new ArrayList();
list.add("1111");
String o = (String) list.get(0);
System.out.println(o.length());
// 可能会导致别的开发人员存储的是 int 类型
list.add(2222);
}
复制代码
上述代码存在的问题:
集合存储的元素需要强制类型转换,才能使用对应的 Api。
编译期语法检测不到我提到的这个问题,在运行时期会有隐患
JDk 1.5
1.7 增加的特性 菱形泛型我掺杂到一起说了
// 有个集合你想保存 String 类型数据
List list =new ArrayList();
list.add("1111");
String s = list.get(0);
System.out.println(s.length());
// 下列会在编译时期就会报错
// list.add(2222);
复制代码是不是喜大普奔,不那么反人类了
泛型
泛型:参数类型化
泛型可以在类上或方法声明,方法上的泛型优先级高于类上的优先级。为便于理解最好不和类上声明相同命名。
静态方法不能使用类上泛型声明,只能在方法上声明
常用的泛型声明(T,E,K,V,?)
?表示不确定的 java 类型
T (type) 表示具体的一个java类型
K V (key value) 分别代表java键值中的Key Value
E (element) 代表Element
S、U、V 等:多参数情况中的第 2、3、4 个类型
代码验证
泛型可以在类上或方法声明,方法上的泛型优先级高于类上的优先级
public class ClientTest{
/**
* 验证泛型可以在类上和方法上声明
* 不能这样
*/
public T2 run2(T k){
System.out.println(k.getName());
System.out.println(k);
return (T2) k;
}
}
复制代码以上验证了泛型可以在类上和方法上声明
package com.fly.study.java.generics;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
/**
* @author 张攀钦
* @date 2019-09-16-00:04
* @description 泛型验证
*/
public class ClientTest{
private ClientTest clientTest;
private Parent parent;
private Son son;
private ClientTest sonClientTest;
private ArgsParent argsParent;
private ArgsSon argsSon;
/**
* 限制方法参数的泛型和返回值类型,泛型优先使用方法上的,当方法上没有使用类上的
* 不能这样
*/
public T2 run2(T k){
System.out.println(k.getName());
System.out.println(k);
return (T2) k;
}
@Before
public void before(){
clientTest = new ClientTest();
parent = new Parent();
parent.setName("parent");
son = new Son();
son.setAge(18);
sonClientTest = new ClientTest<>();
argsParent = new ArgsParent();
argsParent.setName("argsParent");
argsSon = new ArgsSon();
}
@Test
public void test2(){
System.out.println(clientTest.run2(argsParent));
System.out.println(clientTest.run2(argsSon));
// 验证方法上的泛型优先于类上的泛型
sonClientTest.run2(argsSon);
// 下面用法错误
// sonClientTest.run2(son);
}
}
@Data
public class ArgsParent{
private String name;
}
@Data
public class ArgsSon extends ArgsParent{
private Integer age;
}
@Data
public class Parent{
private String name;
}
@Data
public class Son extends Parent{
private Integer age;
}
复制代码我在类上和方法上使用了同一个泛型 T ,但是类型限制不同,类上泛型 T 我在 new 对象的时候是 ClientTest sonClientTest,而我在方法上指定了T 为 ,运行方法的时候,如果参数只能传入 ArgsParent及其子类即可验证我的结论。
// public class ClientTest
// public T2 run2(T k)
// argsSon 为 ArgsParent 的子类对象
sonClientTest.run2(argsSon);
// 下面用法错误,son 为 Son 的实例对象
// sonClientTest.run2(son);
复制代码静态方法不能使用类上泛型声明,只能再方法上声明
public class ClientTest{
/**
* 静态方法上的泛型不能使用类上的,只能再方法上声明泛型
*/
public static void run4(T k){
System.out.println(k);
}
// 语法错误
public static void run4(T k){
System.out.println(k);
}
}
复制代码泛型限定符
字面意思理解泛型限定符很方便,代码中的注释即可说明意思
// 限定参数类型只能为 ArgsParent及其子类
// 语法错误,jdk 1.8
// 限定参数类型只能为 ArgsParent 及其子类
extends ArgsParent>
// 限定参数只能为 ArgsParent 及其父类
extends ArgsParent>
复制代码public class AllGenerics{
public void run1(T t){
System.out.println(t);
}
public void run2(S s){
System.out.println(s);
}
// 限定参数类型只能为 ArgsParent及其子类
public void run3(S s){
System.out.println(s);
}
// 语法错误,
// public void run3(S s) {
// System.out.println(s);
//}
// 限定参数类型只能为 ArgsParent及其子类
public void run4(List extends ArgsParent> s){
System.out.println(s);
}
// 限定参数只能为 ArgsParent及其父类
public void run5(List super ArgsParent> s){
System.out.println(s);
}
}
复制代码
泛型擦除
泛型只在编译期有效,编译之后会对泛型声明进行替换,对于能确定类型的使用确定类型,不确定的使用 Object 代替。
不能确定泛型类型的时候
// 源码
public class AllGenerics{
public void run1(T t){
System.out.println(t);
}
}
// 编译之后的代码可以这样理解
public class AllGenerics{
public void run1(Object t){
System.out.println(t);
}
}
复制代码对使用泛型限定符
public class AllGenerics{
public void run2(S s){
System.out.println(s);
}
}
// 编译之后的代码可以这样理解
public class AllGenerics{
public void run1(ArgsParent t){
System.out.println(t);
}
}
复制代码
代码验证
泛型只在编译时期有效
@Data
public class ClientTestSuperT{
@Test
public void test1() throws Exception{
Listt=new ArrayList<>();
Method add = t.getClass().getMethod("add",Object.class);
// 添加 Integer 类型数字
add.invoke(t,1);
System.out.println(t);
}
}
复制代码泛型擦除使用可以确定的类型替换
package com.fly.study.java.generics;
import org.junit.Test;
import java.lang.reflect.Method;
public class AllGenerics{
public void run2(S s){
System.out.println(s);
}
public void run1(T t){
System.out.println(t);
}
@Test
public void test1() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", Object.class);
// java.lang.NoSuchMethodException:
// com.fly.study.java.generics.AllGenerics.run1(java.lang.String)
// Method method= allGenerics.getClass().getMethod("run1", String.class);
method.invoke(allGenerics,"111");
}
@Test
public void run2() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run2", ArgsParent.class);
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
// java.lang.NoSuchMethodException:
// com.fly.study.java.generics.AllGenerics.run1(java.lang.String)
// Method method= allGenerics.getClass().getMethod("run2", Object.class);
method.invoke(allGenerics,argsParent);
}
}
复制代码验证思路
AllGenerics 编译之后 AllGenerics
// 编译之后为 public void run1(Object t)
public void run1(T t){
System.out.println(t);
}
// 编译之后 public void run2(ArgsParent s)
public void run2(S s){
System.out.println(s);
}
复制代码利用反射获取Method run1,参数 Object 的方法能获取,而获取 run1,参数 String 的找不到
@Test
public void test1() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", Object.class);
method.invoke(allGenerics,"111");
}
复制代码上述代码可以正常运行,验证编译之后代码确实替换了
@Test
public void test1() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run1", String.class);
method.invoke(allGenerics,"111");
}
复制代码上述代码运行报错 java.lang.NoSuchMethodException:com.fly.study.java.generics.AllGenerics.run1(java.lang.String),说明编译之后的代码中没有这个方法
对于使用限定类型的泛型,替换为限定的类型
// 方法编译之后为:public void run2(ArgsParent s)
public void run2(S s){
System.out.println(s);
}
复制代码@Test
public void run2() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
Method method= allGenerics.getClass().getMethod("run2", ArgsParent.class);
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
method.invoke(allGenerics,argsParent);
}
复制代码以上获取到了方法,说明我的猜测正确
@Test
public void run2() throws Exception{
AllGenerics allGenerics =new AllGenerics<>();
ArgsParent argsParent = new ArgsParent();
argsParent.setName("测试");
Method method= allGenerics.getClass().getMethod("run2", Object.class);
method.invoke(allGenerics,argsParent);
}
复制代码代码报错,找不到对应的方法
推荐阅读