java写dnf外掛_dnf卡盟_Java的泛型详解(一)
Java實現DDD中UnitOfWorkdnf卡盟
Java的泛型詳解
泛型的利益
編寫的代碼可以被差別類型的工具所重用。
由于上面的一個優點,泛型也可以削減代碼的編寫。
泛型的使用
簡樸泛型類
public class Pair {
private T first;
private T second;
public Pair() {
first = null;
second = null;
}
public Pair(T first, T second){
this.first = first;
this.second = second;
}
public T getFirst(){
return first;
}
public T getSecond(){
return second;
}
public void setFirst(T first) {
this.first = first;
}
public void setSecond(T second) {
this.second = second;
}
}
上面例子可以看出泛型變量為T;
用尖括號(<>)括起來,并放在類名后面;
泛型還可以界說多個類型變量好比上面的例子 first和second差別的類型:
public class Pair {....}
注: 類型變量的界說需要一定的規范:
(1) 類型變量使用大寫形式,而且要比較短;
(2)常見的類型變量稀奇代表一些意義:變量E 示意聚集類型,K和V示意要害字和值的類型;T、U、S示意隨便類型;
類界說的類型變量可以作為方式的返回類型或者局部變量的類型;
例如: private T first;
用詳細的類型替換類型變量就可以實例化泛型類型;
例如: Pair 代表將上述所有的T 都替換成了String
由此可見泛型類是可以看作通俗類的工廠
泛型方式
我們應該若何界說一個泛型方式呢?
泛型的方式可以界說在泛型類,也可以界說在通俗類,那若是界說在通俗類需要有一個尖括號加類型來指定這個泛型方式詳細的類型;
public class TestUtils {
public static T getMiddle(T... a){
return a[a.length / 2];
}
}
類型變量放在修飾符(static)和返回類型的中心;
當你挪用上面的方式的時刻只需要在方式名前面的尖括號放入詳細的類型即可;
String middle = TestUtils.getMiddle("a", "b", "c");
若是上圖這種情形實在可以省略 ,由于編譯器能夠推斷出挪用的方式一定是String,以是下面這種挪用也是可以的;
String middle = TestUtils.getMiddle("a", "b", "c");
然則若是是以下挪用可能會有問題:
如圖:可以看到變意思沒有辦法確定這里的類型,由于此時我們入參通報了一個Double3.14 兩個Integer1729 和0 編譯器以為這三個不屬于同一個類型;
此時有一種解決辦法就是把整型寫成Double類型
類型變量的限制
有時刻我們不能無限制的讓使用者通報隨便的類型,我們需要對我們泛型的方式舉行限制通報變量,好比如下例子
盤算數組中最下的元素
這個時刻是無法編譯通過的,且編譯器會報錯
由于我們的編譯器不能確定你這個T 類型是否有compareTo這個函數,以是這么能讓編譯器信賴我們這個T是一定會有compareTo呢?
我們可以這么寫 這里的意思是T一定是繼續Comparable的類
由于Comparable是一定有compareTo這個方式,以是T一定有compareTo方式,于是編譯器就不會報錯了
由于加了限制那么min這個方式也只有繼續了Comparable的類才可以挪用;
若是要限制方式的泛型繼續多個類可以加extends 要害字并用&支解如:T extends Comparable & Serializable
限制類型是用&支解的,逗號來支解多個類型變量
類型擦除
豈論什么時刻界說一個泛型類型,虛擬機都市提供一個響應的原始類型(raw type)。原始類型的名字就是刪掉類型參數后的泛型類型。擦除類型變量,并替換限制類型(沒有限制類型的變量用Object)
列如: Pair 的原始類型如下所示
public class Pair {
private Object first;
private Object second;
public Pair() {
first = null;
second = null;
}
public Pair(Object first, Object second){
this.first = first;
this.second = second;
}
public Object getFirst(){
return first;
}
public Object getSecond(){
return second;
}
public void setFirst(Object first) {
this.first = first;
}
public void setSecond(Object second) {
this.second = second;
}
}
由于上面的T是沒有限制變量,于是用Object取代了;
若是有限制變量則會以第一個限制變量替換為原始類型如:
public class Interval implements Serializable{
private T lower;
private T upper;
}
原始類型如下所示:
public class Interval implements Serializable{
private Comparable lower;
private Comparable upper;
}
翻譯泛型表達式
上面說到泛型擦除類型變量后對于無限制變量后會以Object來替換泛型類型變量;
然則我們使用的時刻并不需要舉行強制類型轉換;
原因是編譯器已經強制插入類型轉換;
例如:
Pair buddies = ...;
Employee buddy = buddies.getFirst();
擦除getFirst的返回類型后將返回Object類型,然則編譯器自動插入Employee的強制類型轉換,編譯器會把這個方式挪用翻譯為兩條虛擬機指令;
對原始方式Pair.getFirst的挪用
將返回的Object類型強制轉換為Employee類型;
我們可以反編譯驗證一下
要害的字節碼有以下兩條
9: invokevirtual #4 // Method com/canglang/Pair.getFirst:()Ljava/lang/Object;
12: checkcast #5 // class com/canglang/model/Employee
虛擬機指令寄義如下:,dnf論壇,
invokevirtual:虛函數挪用,挪用工具的實例方式,憑據工具的現實類型舉行派發,支持多態;
checkcast:用于檢查類型強制轉換是否可以舉行。若是可以舉行,checkcast指令不會改變操作數棧,否則它會拋出ClassCastException異常;
由此我們可以驗證了上述的結論,在反編譯后的字節碼中看到,當對泛型表達式挪用時,虛擬機操作如下:
對于工具的現實類型舉行替換泛型;
檢查類型是否可以強制轉換,若是可以將對返回的類型舉行強制轉換;
翻譯泛型方式
類型擦除也會出現在泛型方式內里
public static T min(T[] a)
類型擦除后
public static Comparable min(Comparable[] a)
此時可以看到類型參數T已經被擦除了,只剩下限制類型Comparable;
方式的類型擦除帶來了兩個龐大的問題,看下面的示例:
public class DateInterval extends Pair {
public void setSecond(LocalDate second){
System.out.println("DateInterval: 進來這里了!");
}
}
此時有個問題,從Pair繼續的setSecond方式類型擦除后為
public void setSecond(Object second)
這個和DateInterval的setSecond顯著是兩個差別的方式,由于他們有差別的類型的參數,一個是Object,一個LocalDate;
那么看下面一個列子
public class Test {
public static void main(String[] args) {
DateInterval interval = new DateInterval();
Pair pair = interval;
pair.setSecond(LocalDate.of(2020, 5, 20));
}
}
Pair引用了DateInterval工具,以是應該挪用DateInterval.setSecond。
我們看一下運行效果
然則看了反編譯的字節碼可能發現一個問題:
17: invokestatic #4 // Method java/time/LocalDate.of:(III)Ljava/time/LocalDate;
20: invokevirtual #5 // Method com/canglang/Pair.setSecond:(Ljava/lang/Object;)V
這里可以看到此處字節碼挪用的是Pair.setSecond
這里有個主要的觀點就是橋方式
引用Oracle中對于這個征象的注釋
為了解決此問題并在類型擦除后保留通用類型的?多態性,
Java編譯器天生了一個橋接方式,以確保子類型能夠按預期事情。
對于DateInterval類,編譯器為setSecond天生以下橋接方式:
public class DateInterval extends Pair {
// Bridge method generated by the compiler
//
public void setSecond(Object second) {
setSecond((LocalDate)second);
}
public void setSecond(LocalDate second){
System.out.println("DateInterval: 進來這里了!");
}
}
那么我們若何驗證是否天生這個橋方式呢?我們可以反編譯一下DateInterval.java看一下字節碼;
public void setSecond(java.lang.Object);
descriptor: (Ljava/lang/Object;)V
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #5 // class java/time/LocalDate
5: invokevirtual #6 // Method setSecond:(Ljava/time/LocalDate;)V
8: return
我截取了部門發現在 DateInterval的字節碼中簡直會有一個橋方式,同時驗證了上面的問題;
總結:
虛擬機中沒有泛型,只有通俗的類和方式
所有的類型參數都用他們的限制類型替換
橋方式被合成來保持多態
為保持類型安全性,必要時插入強制類型轉換,dnf助手Kubernetes學習筆記(二):Pod、標簽、注解
總結
以上是生活随笔為你收集整理的java写dnf外掛_dnf卡盟_Java的泛型详解(一)的全部內容,希望文章能夠幫你解決所遇到的問題。

- 上一篇: 鸡蛋黄瓜减肥法
- 下一篇: 花牛苹果的功效与作用、禁忌和食用方法