kafka--Struct Streaming--hdfs案例
技術標籤:Javajava程式語言pythontypescriptvue
型別擦除
編譯器會在編譯期間擦除泛型語法並相應的做出一些型別轉換動作,生成的Java位元組程式碼中是不包含泛型中的型別資訊的
如 下面變數t的型別轉換成了Object
public class Generic<T> {
private T t;
}
再如
List<Integer> list1=new ArrayList<>(); List<String> list2=new ArrayList<>(); System.out.println(list1.getClass());//class java.util.ArrayList System.out.println(list1.getClass()==list2.getClass());//true
泛型類並沒有自己獨有的Class類物件,比如並不存在List<String>.class或是List<Integer>.class,而只有List.class
型別擦除的基本過程:
- 首先找到用來替換型別引數的具體類,這個具體類一般是Object,如果指定了型別引數的上界的話,則使用這個上界
- 把程式碼中的型別引數都替換成具體的類,同時去掉出現的型別宣告,即去掉<>的內容
指定了上界的情況:
public class Generic<T extends Integer> {
private T t;
}
注意:不能指定下界
思考:型別擦除之後,get()返回的應該是Object,那為什麼可以得到String型別
List<String> list=new ArrayList<>();
list.add("hh");
String s=list.get(0);
檢視反編譯結果
在型別擦除後,會自動生成
checkcast
指令進行強制型別轉換
和直接
String s=(String)list.get(0);
的位元組碼是一樣的
萬用字元和上下界
- 在使用泛型類的時候,既可以指定一個具體的型別,如List<String>就聲明瞭具體的型別是String;也可以用萬用字元?來表示未知型別,如List<?>就聲明瞭List中包含的元素型別是未知的
- 萬用字元所代表的其實是一組型別,但具體的型別是未知的。List<?>所宣告的就是所有型別都是可以的
- 但是List<?>並不等同於List<Object>。List<Object>實際上確定了List中包含的是Object及其子類,在使用的時候都可以通過Object來進行引用。而List<?>則其中所包含的元素型別是不確定。其中可能包含的是String,也可能是 Integer。如果它包含了String的話,往裡面新增Integer型別的元素就是錯誤的
- 正因為型別未知,就不能往List中新增元素,因為編譯器無法知道具體的型別是什麼。但是對於 List<?>中的元素確總是可以用Object來引用的,因為雖然型別未知,但肯定是Object及其子類
ArrayList<?> arrayList=new ArrayList<>();
Object o=arrayList.get(0);
arrayList.add(null);
arrayList.add(1);//編譯錯誤
因為對於List<?>中的元素只能用Object來引用,在有些情況下不是很方便。在這些情況下,可以使用上下界來限制未知型別的範圍。 如List<? extends Number>說明List中可能包含的元素型別是Number及其子類。而List<? super Number>則說明List中包含的是Number及其父類
引數擦除後,List<Integer>和List<Number>都是List型別,但在編譯過程中這兩個是不同的型別
List<Integer> list=new ArrayList<>();
List<Number> list1=new ArrayList<>();
list1=list;//編譯錯誤
Java中 陣列是協變的,即可以把子類陣列賦值給父類陣列
Number[] numbers = new Integer[10];
而泛型卻不可以
ArrayList<Number> numbers= new ArrayList<Integer>();//編譯錯誤
泛型也不能建立具體型別的陣列
如:List<Integer>[] li2 = new ArrayList<Integer>[];//編譯錯誤 List<Boolean> li3 = new ArrayList<Boolean>[];//編譯錯誤
List<Integer>
和List<Boolean>
在 jvm 中等同於List<Object>
,所有的型別資訊都被擦除,程式也無法分辨一個數組中的元素型別具體是List<Integer>
型別還是List<Boolean>
型別但是藉助無限定萬用字元
<?>
卻可以List<?>[] li3 = new ArrayList<?>[10]; li3[0] = new ArrayList<Integer>(); li3[1] = new ArrayList<String>(); List<?> v = li3[1];
List<Integer> list=new ArrayList<>();
List<Number> list1=new ArrayList<>();
list1=list;//假設編譯通過
list1.add(12.1);
假設 Java 允許泛型協變,那麼上述程式碼在編譯器看來是沒問題的,但執行時就會出現問題。這個 add 方法實際上就將一個浮點數放入了整型容器中了,雖然由於型別擦除並不會對程式執行造成問題,但顯然違背了泛型的設計初衷,容易造成邏輯混亂,所以 Java 乾脆禁止泛型協變。
假如有某種需求,方法既要支援子類泛型作為形參傳入,也要支援父類泛型作為形參傳入,可以採用萬用字元
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
List<Number> list1 = new ArrayList<>();
f(list);
f(list1);
}
void f(List<? extends Number> list) {
}
引入泛型之後的型別系統增加了兩個維度:一個是型別引數自身的繼承體系結構,另外一個是泛型類或介面自身的繼承體系結構。第一個指的是對於 List<String>
和List<Object>
這樣的情況,型別引數String
是繼承自Object
的。而第二種指的是 List
介面繼承自Collection
介面。對於這個型別系統,有如下的一些規則:
相同型別引數的泛型類的關係取決於泛型類自身的繼承體系結構。即
List<String>
是Collection<String>
的子型別,List<String>
可以替換Collection<String>
。這種情況也適用於帶有上下界的型別宣告。 當泛型類的型別宣告中使用了萬用字元的時候, 其子型別可以在兩個維度上分別展開。如對Collection<? extends Number>
來說,其子型別可以在Collection這個維度上展開,即List<? extends Number>
和Set<? extends Number>
等;也可以在Number這個層次上展開,即Collection<Double>
和Collection<Integer>
等。如此迴圈下去,ArrayList<Long>
和HashSet<Double>
等也都算是Collection<? extends Number>
的子型別。 如果泛型類中包含多個型別引數,則對於每個型別引數分別應用上面的規則。
Collection<Number>可替換List<Number>
Collection<? extends Number>可替換List<? extends Number>
Collection<? extends Number>可替換List<Number>
Collection<? extends Number>可替換List<Integer>
Collection<? extends Number>可替換List<? extends Integer>
Collection<?>可替換List<Number>
理解了上面的規則之後,ArrayList<Number>是ArrayList<?>的子型別
ArrayList<Number> list = new ArrayList<>();
ArrayList<?> arrayList = list;//不會有編譯錯誤
上界 extends
public void testAdd(List<? extends Parent> list){
list.add(new Parent());//編譯錯誤
list.add(new Child1());//編譯錯誤
list.add(new Child2());//編譯錯誤
Parent parent = list.get(0);
}
傳入方法的引數可能是List<Parent>
,也可能是List<Child1>
、List<Child2>
假如傳入的引數是List<Child1>
,那麼會發生型別不相容的問題
但是卻可以取得List
中的元素,因為總可以用Parent
來接
下界 super
List<? super Animal> 是 List<? super Bird>的子型別
List<Animal> 是 List<? super Animal>的子型別
public void testAdd(List<? super Parent> list){
list.add(new GrantParent1());//編譯錯誤
list.add(new GrantParent2());//編譯錯誤
list.add(new Parent());
list.add(new Child());
Object object = list.get(0);
}
傳入方法的引數可能是List<Parent>
,也可能是List<GrantParent1>
、List<GrantParent2>
假如傳入的引數是List<GrantParent1>
,List<GrantParent2>
與List<GrantParent1>
不相容,所以只能新增Parent
及其子類
如果要獲取List
中的元素,Parent
的父類那麼多,只能用Object
來接
歸根結底可以用一句話表示,那就是編譯器可以支援向上轉型,但不支援向下轉型
上界的list只能get,不能add(確切地說不能add出除null之外的物件,包括Object)add會報編譯錯誤
下界的list只能add,不能get