泛型机制是咱们开发中的常用技巧,也是面试常见问题

不过泛型机制这个知识点也比较冗繁又不成系统,学了简单忘
本文从几个问题启航收拾Java泛型机制知识点,假定对你有用,欢迎点赞~

本文首要包括以下内容
1.咱们为什么需求泛型?
2.什么是泛型擦除及泛型擦除带来的一些json字符串问题,字节码和机器码的差异retrofit怎样取得擦除后的类型,Gson怎样取得擦除后jsonp跨域原理的类型?
3.什么是PECS准则

本文目录如下

【知识点】Java泛型机制7连问

1.咱们为什么需求泛型?

咱们为什么需求泛型,即泛型有什么用?
首要举两个比如

1.1 求和函数

实践开发中,常常有数值类型求和的需求,例如结束int类型的加法, 有时分还需求结束long类型的求和 假定还需求double类型的求和,apple id暗码重置又需求从头在重载一个输入是double类型的add方法。

public int addInt(int x,int y){
return xapp id注册+y;
}
public float addFloat实例化是什么意思(float x,float y){
return x+字节码y;
}

假定没有泛型json格局,咱们需求写不少重复代码

1.2 List中增加元素

List list = new ArrayL实例化目标ist();
list.add("mark");
list.add("OK");
list.add(100);
for (int i = 0; i < lapple payist.sizejson(); i++) {
S实例化一个类tring name = liapple payst.get(i); // 1
System.out.println("name:" + name);
}

1.list默许是Object类型,因而能够存恣意类型数据
2.可是当取出来时,咱们并不知道取出元素的类型,就需求进行强制类型转化了,而且简单犯错

1.3 泛型机制的实例化需求利益

从上面的两个比如咱们能够直观的得出泛型机制的利益
1.运用泛型能够编写模板代码来习气恣意类型,减json文件是干什么的少重复json解析代码
2.运用时不用对类型进行强apple tv制转化,便当且减少犯错时机

2.泛型擦除

2.1 什么是泛型擦除?

大家都知道,Java的泛型是伪泛型,这是由于Java在编译期间,悉数的泛型信息都会被擦掉,正确理解泛型概念的首要条件是理解类型擦除json格局怎样翻开
Java的泛型基本字节码是什么上都是在编译器这个层次上结束的,在生成的字节码中是不包括泛型中的类型信息的,运用泛型的时分加上java模拟器类型参数,在编译器编字节码译的时分会去掉,这个进程就是泛型擦除。

举个比如:

public class Tjson字符串est {
public static void main(String[] args) {
ArrayList<String> list1 = new ArrayList<String>();
lijavaapi中文在线看st1.add("abc");java初学
ArrayList<Integer&gt实例化是什么意思; list2 = new Arrajava模拟器yList<Integer>();
list2.add(123);
System.out.println(list1.getClass() == list2.getClass());
}
}

如上list1.getClass==list2.gejava难学吗tClass回来trapple storeue,阐明泛型类型StringInteger都被擦除掉了,只剩下原始类型
Java的泛型也能够被称作是伪泛型

  • 真泛型:泛型中的类型是javaapi中文在线看实在存在的。
  • 伪泛型:仅于编译时类型查看,在工作时擦除类型信息。

看到这儿咱们能够天然地引出下一个问题,为什么Javajson文件是干什么的的泛型是伪泛型,为什么要java言语这样结束?

2.2 为什么需求泛型擦除?

泛型擦除看起来有些反直觉,有些古怪。java言语为什么java言语Java不能像C#相同结束实在的json解析泛型呢?为什么Java的泛型要用”擦除”结束
单从技能来说,实例化servlet类反常Java是彻底100%能结束咱们所说的真泛型,而之所以挑选运用泛型擦除首要是从API兼容的视点考虑的
导致Java 5引进的泛型javaee选用擦除式结束的底子原因是兼容性上的取舍,而不是“结束不了”的问题。

举个比如,实例化类Java1.4.2都没有支撑泛型,而java工作培训班Java 5遽然支撑泛型了,要让曾经编译的程序在新版别的JRE还能字节码正常工作,就意味着曾经没有的捆绑不能遽然冒出来。
假定在没有泛型的Java里,咱们有程序运用了java.util.ArrayList类,而且咱们利用了它能够存异质元素的特性:

ArrayList tapple storehings = new ArrayList();
things.add(Integer.valueof(42));
things.add("Hello World")

为了这段代码在Jjson格局怎样翻开ava 5apple watch入泛型之后还有必要要继续能够工作,有两种规划思路
1.需求泛型化的类型(首要是容器(Collections)类型),曾经有的就坚持不变,然后平行地加一套泛型化版别的新类型;
2.直接把已有的字节码文件的扩展名是什么类型泛型化,让悉数需求泛型化的已有类型都原地泛型化,实例化目标是什么意思不增加任何平行于已有类型的泛型版。

.NET1.1 -> 2.0的时分挑选了上面选项的1,而Java则挑选了2。

Java规划者的视点实例化需求实例化,这个取舍很明白。
.NET1.1 -> 2.0的时分,实践的运用代码量还很少(相对Java来说),而且整个系统都在微软的操控下,要做改变比较简单;
Java1.4.2 -> 5.0的时分,Java已经有许多字节码是什么意思程序部署在出产环境中java难学吗,已经有许多运用和库程序的代码。
假定这些代码在新版别的Java中,为了运用Java的新javaapi中文在线看功用(例如泛型)而有必要做许多源码层字节码文件是与渠道无关的什么文件修改,那么新功用的普及速度就会大受影响。

2.3 泛型擦除后re实例化一个类trofit是怎样获取类型的javascript?

RJSONetrofit是怎样传递泛型信息的?
上一段常见的网络接口央求代码:

pubjson是什么意思lic interface GitHubjava模拟器Service {
@GET("users/{usjavascripter}/repos")
Call<List<Repo>> listRepos(@Path("usapp storeer") String user)json是什么意思;
}

运用jad查看反编译后的class文件:

import retrofit2.Call;
papp id注册ublic interface GitHu实例化目标是什么意思bService
{
public abstract Call listR实例化类epos(String s);
}

能够看到class文件中已经将泛型信息给擦除了,那么Retrofit是怎样拿到C实例化all<List>的类型信息的?
咱们看一下retrofit的源javascript

  st实例化servlet类反常atic <T&gjson格局t; Sejson格局怎样翻开rviceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
...
Type returnType = method.getGenericReturnType();
...
}
public Type getGenericReturnType()jsonp {
// 依据 Signature 信息 获取 泛型类型 
if (getGenericSignature() != null) {
return getGenericInfo().getReturnType();
} else {
return getRetjava言语urnType();
}
}java模拟器

能够看出,retrofit是经过getGenericReturnTapple tvype来获取类型信息的
jdk字节码文件是与渠道无关的什么文件ClassMethodField 类供给了一系apple id列获取 泛型类型的相关方法。
Methojavaapi中文在线看d为例,getGenericReturnType获取带泛型实例化信息的回来类型 、 getGenericPaapple id暗码重置rameterTypes获取带泛型信息的参数类型。

问:泛型的信息不是被擦除了吗?
答:是被擦除了, 可是某些(声明侧的泛型,接下来解释) 泛型信息会被class文件 以Signature的方法 保存在Class文件的Constant pool中。

经过javap指令 能够看到在Constant pooljsonp#5 S实例化servlet类反常ignature记录了泛型的类型。

Constant pool:
#1 = Class              #16            //  com/example/diva/leet/GitHubSJSONervice
#2 = Class              #17            //  java/l字节码ang/Object
#3 = Utf8               listRepos
#4 = Utf8               (Ljava/lang/String;)Lretrofit2/Call;
#5 = Utf8               Signature
#6 = Utf8               (字节码是什么Ljava/lang/String;)Lretrofit2/Call<Ljava/util/List<Lcom/example/diva/leet/javaapi中文在线看Repo;>;>;
#7 = Utf8               RuntimeVisibleA实例化目标nnotations
#实例化需求8 = Utf8               Lretrofit2/http/GET;
#9 = Utf8               value
#10 = Utf8               users/{user}/repos
#11 = Utf8               RuntimeVisibleParametjson格局erAnnotations
#12 = Utf8               Lretroapple tvfit2/http/javaapi中文在线看Path;
#13 = Utf8               user
#14 = Utapp id注册f8               SourceFile
#15 = Utf8               GitHubService.java
#16 = Utf8               com/example/diva/leet/GitHubService
#17 = Utf8               java/lang/Object
{
public abstract retrofit2.Call<java.util.List<com.example.diva.le实例化类et.Repo>> listRepos(java.lang.Strjson格局怎样翻开ing);
flags: ACC_PUBLIC, ACC_ABSTRACT
Signature: #6                           // (Ljava/lang/String实例化一个类;)Lretrofit2/Call<Ljava/util/List<Lcom/example/diva/lee字节码和机器码的差异t/Repo;>;>;
RuntimeVisibleAnnotations:
0: #8(#9=s#10)
Runtime实例化目标的关键字VisibleParameterAnnotations:
parameter 0:
0: #12(#9=s#13)
}

这就是咱们retroapp id注册fit中能够获取泛型字节码文件是与渠道无关的二进制码类型的原因

2.4 Gson解析为什么要传入内部类

Gson是咱们常用的json解析库,一般是这样运用的

	// Gapple watchson 常用的状况
public  List<String> parse(String jsonStr){
List<String> topNews =  new Gson().fromJson(jsonStr, new TypeTok字节码文件en<Liapp id注册st&l实例化servlet类反常t;String>>() {}.getType());
rejson字符串turn topNews;
}

咱们这儿能够提出两个json格局问题
1.Gson是怎样获取泛型类型的,也是经过Signature吗?
2.为Java什么Gson解析要传入匿名内部类?这实例化是什么意思apple tv起来有些古怪

2.4.1 那些泛型信息会被保存,哪些是实在的擦除了?

上面咱们说了,声明侧泛型会被记录在Class文件的Constant字节码是什么意思 pool中,运用侧泛型则不会

声明侧泛型首要指以下内容
1.泛型类,或泛型接口的声明
2.带有泛型参数的方法
3.带有泛型参数的成员变量

运用侧泛型
也就是方法的局json格局怎样翻开部变量,方法调用时传入的变量。

Gson解析时传入的参数归于运用侧泛型,因而不能经过Signature解析

2.4.2 为什么Gson解析要传入匿名内部类

依据以上的总结,方法的局部变量的泛型是实例化是什么意思不会被保存的
Gson是怎样apple官网获取到List<S实例化目标的关键字tring>的泛型信息Stringjavaee的呢?
Clajavaapi中文在线看ss类供给了一个方法public Type getjava怎样读GenericSuperclass() ,能够获取到带泛型信息的父类Tjavaeeype
也就是说javaclass文件会保存承继的父类或许接口的泛型信息。

所以Gson运用了一个美好的方法来获取实例化泛型类型:
1.创立一个泛型笼统类TypeTok字节码文件是与渠道无关的二进制码en <T&gtjava言语; ,这个笼统类不存在笼统方法,由于匿名内部实例化servlet类反常类有必要承继自字节码文件是与渠道无关的什么文件笼统类或许接口。所以才界说为笼统类。
2.创立一个 承继自TypeToken的匿名内部类, 并实例化泛型参数TypeToken<String>
3.经过class类的public Type实例化一个类 ge字节码是什么tGenericSuperclass()方法,获取带泛型信息的父类Type,也就是TypeToken&l实例化目标的关键字t;String>

总结:Gsonapple watch利用子类会保存父类classJSON的泛型参数信息的特征。 经过匿名内部类结束了泛型参数的传递。

3.什么是PECS准则?

3Apple.1 PECS介绍

PECS的意思是Producer Extend Consumer Super,简略理解为假定是出产者则运用Extendjava言语假定是顾客则运用Super,不过,这究竟是啥意思呢?

PECS是从集结的视点启航的
1.假定你仅仅从集结中取数据,那么它是个出产者,你应该用extend
2.假定你仅仅往集结中加数据,那么它是个顾客,你应该用super
3.假json是什么意思设你往集结中字节码既存又取,那么你不应该用extend或许super

让咱们经过一个典型的比如理解一下究竟什么是ProducerConsumer

public clasjava怎样读s Collections {
public static <T&apple watchgt; void copy(List<? super T> dest, List&l字节码是什么意思t;? extends T> src)字节码目标   {
for (int i=0; i<src.size(); i++) {
dest.set(i, src.get(i));
}json是什么意思
}
}

上面的比如中将src中的数据复制到dest中,这儿src就是出产者,它「出产」数据,dest是顾客,它「消费」数据。

3.2 为什么需求P字节码是什么意思ECS

运用PECS首要是为了结束集实例化数组结的多态
举个比如,现在有这样一个需求,将生果篮子中悉数生果拿出来(即取出集结全字节码文件部元素并进行操作)

public static void getOutFruits(List<Fruit> basket){
forjavascript (Fruit fruit : basket) {
Sy字节码文件的扩展名是什么stem.out.println(fruit);
//...do somet字节码hing other
}
}java面试题
List<Fruit> fruitBasket = new ArrayList<Fruit>();
getOutFruits(fruitBasket);//成功
List<Apple> appleBasket = new ArrayList<Apjson文件是干什么的pl实例化需求e>();字节码文件能够直接在操作系统上运转
getOutFru字节码文件能够直接在操作系统上运转its(appleBasket);//编译过失

如上所示:
1.将List<Apple>传递给List<Fruit>字节码文件是与渠道无关的二进制码会编译过失。
2.由于尽管FruitApple的父类,可是List<Apple>List<Frapp id注册uit>之间没有承继联络
3.由于这种捆绑,咱们不能很好的结束取出生实例化类果篮子中的悉数生果需求,总不能每个类字节码和机器码的差异型都写一遍相同的代码吧?

运用extend能够便当地处理这个问题

/**参数运用List&字节码文件lt;? extends Fruit>**/
public static void getOutFruits(List<? extends Fruit>apple pay basket){
for (Fruit fruit : basket) {
Sysapple idtem.out.println(fruit);
//...do something other
}
}
public static void main(String[] args) {
List<Fruit> fruitBas实例化数组ket =app store new ArrayList<>();
fruitBasket.add(new Fruit());
getOutFruits(fruitBasket);
List&ltapple官网;Apple> appljson字符串eBjsonpasket = new ArrayList<>();
appleBasket.add(new Apple());
getOutFruits(appleBasket);//编译正确
}

List<? extends Fruit&gtjson数据;,一起兼容了List&lJSONt;Fruit>List<Apple>,咱们能够理解为List<? extends Fruit>现在是List<Fruit>List<Apple>的超类型apple pay(父类型)
经过这种方法就结束了泛型集结的多态

3.3java面试题 小结

  • List<? exten实例化目标ds Fr字节码uit>的泛型集结中,关于元Java素的类实例化目标有几种方法型,编译器只能知道元素是承继自Fruit,详细是Fruit实例化数组的哪个子类是无法知道的。 所以「向一个无法知道详细类型的泛型集结中刺进元素是不能经过编译的」。可是由于知道元素是承继自Fruit,所以从这个泛型集结中取Fruit类型的元素是能够的。
  • List<? super Apple>的泛型集结中,元素的类型是Apple的父类,但无法知道是哪个详细的实例化目标有几种方法父类,因而「读取元素时无法供认以哪个父类进行读取」。 刺进元素时能够刺进AppleApple的子类,由于这个集结中的元素都是Apple的父类,子类型是能够赋值给父类型的。

有一个比较好记的口诀:
1.只读不可写时,运用List<? extends Fruit>:Producer
2.只写不可读时,运用List<? super实例化类 Apple>:Consumer

总得来说,List<Fruit>List<Apjson字符串ple>之间没有任何承继联络。API的参数想要一起兼容2java面试题者,则实例化需求只能运用PECS准则。这样做提升了API的灵活性,结束了泛型集结的多态字节码是虚拟机的机器码
当然字节码是虚拟机的机器码,为了提升了灵活性,天然献身了部分功用。鱼和熊掌不能兼得。

总结

本文收拾了Java泛型机制这个知识点,答复了如下几个问题
1.咱们为什么需求泛型?
2.什jsonp么是泛型擦除?
3.为什么需求泛型擦除?
4.泛型擦除后retrofit怎样取得类型的?
5.Gson解析为什么要传入内部类
6.什么是PECS准则?
7.为什么需求PECS准则?

假定对您有所协助,欢迎点赞,字节码文件是与渠道无关的二进制码谢谢~

参考资料

Jjson格局ava 不能json是什么意思结束实在泛型的原因是什么?
java 的泛型擦除与 Type字节码是什么意思Token
关于PECS准则