日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java 泛型 擦除_Java泛型和类型擦除

發布時間:2025/3/15 java 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 泛型 擦除_Java泛型和类型擦除 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一 前言:初識泛型

廢話不說,先來看一段代碼:

public class Holder {

private Object data;

public Holder(Object data ){

this.data = data;

}

public void setData(Object data) {

this.data = data;

}

public Object getData() {

return data;

}

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

}

}

class SomeNode{}

Holder類是一個容器,它的作用是用來保存其他類的,這里我們用它來保存SomeNode類,隨后把它取出來,編譯運行,結果如下:

Error:(21, 43) java: incompatible types

required: SomeNode

found: java.lang.Object

意思是,需要的是SomeNode,取出來的卻是Object,如此看來,如果我想保存SomeNode類,就只能把data聲明為SomeNode:

private SomeNode data;

這就意味著我們需要為每一個類創造一個Holder,這肯定是不行的,于是泛型的作用來了,泛型,可以理解為任何類型,意思是我可以聲明一個可以容納任何類型的容器:

public class Holder {

private T data;

public Holder(T data ){

this.data = data;

}

public void setData(T data) {

this.data = data;

}

public T getData() {

return data;

}

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

}

}

class SomeNode{}

注意寫法,在類聲明后面加個就行了,你也可以加,只是一個占位符,形參而已。然后我們再把它取出來:

Process finished with exit code 0

程序沒有報錯,如果這時候我們使用holder的set()方法去插入設置一些非SomeNode類型的值,代碼如下:

public static void main(String[] args){

Holder holder = new Holder(new SomeNode());

SomeNode someNode = holder.getData();

holder.setData("AAAA");

}

看結果:

Error:(22, 15) java: method setData in class Holder cannot be applied to given types;

required: SomeNode

found: java.lang.String

reason: actual argument java.lang.String cannot be converted to SomeNode by method invocation conversion

泛型機制就自動為我們報錯,很方便。

二 泛型類:元組(Tuple),返回多個對象

熟悉python的同學都知道元組的概念,它是一個只讀列表,在返回多個結果時是很有用的,我們利用泛型特性來創造一個包含兩個對象的元組:

public class Tuple {

public static void main(String[] args){

TwoTuple t = new TwoTuple("Monkey",12);

System.out.println(t.toString());

}

}

class TwoTuple{

final A first;

final B second;

public TwoTuple(A a,B b){

first = a;

second = b;

}

public String toString(){

return "("+first+","+second+")";

}

}

來看結果:

(Monkey,12)

是不是很方便:)如果想要一個長度為3的元組可以這么寫:

public class Tuple {

public static void main(String[] args){

ThreeTuple t = new ThreeTuple("Dog",12,true);

System.out.println(t.toString());

}

}

class TwoTuple{

final A first;

final B second;

public TwoTuple(A a,B b){

first = a;

second = b;

}

public String toString(){

return "("+first+","+second+")";

}

}

class ThreeTuple extends TwoTuple{

final C three;

public ThreeTuple(A a,B b,C c){

super(a,b);

three = c;

}

public String toString(){

return "("+first+","+second+","+three+")";

}

}

結果如下:

(Dog,12,true)

三 泛型接口

泛型接口的定義和泛型類的定義類似,我們來定義一個生成器接口:

public interface Generator {

T next();

}

接著我們實現這個接口,來生成斐波拉契數:

public class Fib implements Generator {

private int count = 0;

@Override

public Integer next() {

return fib(count++);

}

private int fib(int n){

if (n<2)

return 1;

else

return fib(n-2) + fib(n-1);

}

public static void main(String[] args){

Fib f = new Fib();

for (int i=0;i<100;i++){

System.out.println(f.next());

}

}

}

四 泛型方法

比起泛型類,我們更推薦去使用泛型方法,泛型方法定義起來也很簡單,我們只需將泛型參數放在返回類型前面即可:

public class GenericMethods {

public void f(T x){

System.out.println(x.getClass().getName());

}

public static void main(String[] args){

GenericMethods g = new GenericMethods();

g.f("Hello");

g.f(100);

g.f(true);

}

}

這里我們定義了一個泛型方法f(),并使用getClass獲取類的相關信息(關于Class對象的知識點這里),來看結果:

java.lang.String

java.lang.Integer

java.lang.Boolean

這里還要注意一下Varargs(變長參數)機制和泛型的結合:

public class GenericVarargs {

public static List makeList(T...args){

List list = new ArrayList();

for (T item : args){

list.add(item);

}

return list;

}

public static void main(String[] args){

List list = makeList("A","B","C","D");

System.out.println(list);

}

}

結果如下:

[A, B, C, D]

六 類型擦除

在認識類型擦除之前,我們首先要明白編譯器對泛型的處理有兩種方式:

1.Code specialization

在實例化一個泛型類或者泛型方法是都生成一份新的字節碼,比如對于List,List,List產生三份不同的字節碼。

2.Code sharing

對每個泛型類只生成唯一的一份目標代碼;該泛型類的所有實例都映射到這份目標代碼上,在需要的時候執行類型檢查和類型轉換。參考文章

C++的模板是典型的Code specialization實現,而Java泛型則是Code sharing實現,將多種泛型類形實例映射到唯一的字節碼表示是通過類型擦除(type erasue)實現的。對擦除更通俗的理解就是:編譯器生成的bytecode是不包涵泛型信息的。我們看下面的代碼:

public class ErasedType {

public static void main(String[] args){

Class c1 = new ArrayList().getClass();

Class c2 = new ArrayList().getClass();

System.out.println(c1 == c2);

}

}

結果如下:

true

也就是說我們在實例化ArrayList和實例化ArrayList時是共享一份目標代碼的,泛型類類型信息在編譯的過程中被擦除了。對于JVM來說,它只看到一份ArrayList(原始類型)而已。我們還可以從反射的角度來理解類型擦除:

public class ErasedType {

public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

List list = new ArrayList();

list.add("ABC");

list.getClass().getMethod("add",Object.class).invoke(list,123);

System.out.println(list);

}

}

看結果:

[ABC, 123]

我們很順利的把Integer型的123插入到了String的List里:)

七 后記

由于類型擦除的存在,我們往往會在使用泛型特性的時候遇到一些詭異的問題,由于篇幅原因,這里不展開了:)我將在另外一篇文章中集中的總結一下這方面的問題。

我的微信號是aristark,歡迎交流指正!

總結

以上是生活随笔為你收集整理的java 泛型 擦除_Java泛型和类型擦除的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。