欧美free性护士vide0shd,老熟女,一区二区三区,久久久久夜夜夜精品国产,久久久久久综合网天天,欧美成人护士h版

首頁綜合 正文
目錄

柚子快報激活碼778899分享:

柚子快報激活碼778899分享:

http://yzkb.51969.com/

} }

//編譯時生成的字節(jié)碼文件翻譯過來大致如下 class MyNode extends Node {

public MyNode(Integer data) { super(data); } // 編譯器生成的橋接方法 public void setData(Object data) { setData((Integer) data); }

public void setData(Integer data) { System.out.println(“MyNode.setData”); super.setData(data); } }

3、偽泛型

Java 中的泛型是一種特殊的語法糖,通過類型擦除實現(xiàn),這種泛型稱為偽泛型,我們可以反射繞過編譯器泛型檢查,添加一個不同類型的參數(shù)

//反射繞過編譯器檢查 public static void main(String[] args) {

List stringList = new ArrayList<>(); stringList.add(“erdai”); stringList.add(“666”);

//使用反射增加一個新的元素 Class aClass = stringList.getClass(); try { Method method = aClass.getMethod(“add”, Object.class); method.invoke(stringList,123); } catch (Exception e) { e.printStackTrace(); }

Iterator iterator = stringList.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } //打印結果 erdai 666 123

4、泛型擦除進階

下面我拋出一個在工作中經(jīng)常會遇到的問題:

在進行網(wǎng)絡請求的時候,傳入一個泛型的實際類型,為啥能夠正確的獲取到該泛型類型,并利用 Gson 轉換為實際的對象?

答:是因為在運行期我們可以使用反射獲取到具體的泛型類型

What? 泛型不是在編譯的時候被擦除了嗎?為啥在運行時還能夠獲取到具體的泛型類型?樂?

答:泛型中所謂的類型擦除,其實只是擦除 Code 屬性中的泛型信息,在類常量池屬性(Signature 屬性、LocalVariableTypeTable 屬性)中其實還保留著泛型信息,而類常量池中的屬性可以被 class 文件,字段表,方法表等攜帶,這就使得我們聲明的泛型信息得以保留,這也是我們在運行時可以反射獲取泛型信息的根本依據(jù)

//這是反編譯后的 JavaGenericClass.class 文件,可以看到 T public class JavaGenericClass {

private T a;

public JavaGenericClass(T a) { this.a = a; }

public T getA() { return a; }

public void setA(T a) { this.a = a; }

//… }

注意:Java 是在 JDK 1.5 引入的泛型,為了彌補泛型擦除的不足,JVM 的 class 文件也做了相應的修改,其中最重要的就是新增了 Signature 屬性表和 LocalVariableTypeTable 屬性表

我們看下下面這段代碼:

class ParentGeneric {

}

class SubClass extends ParentGeneric{

}

class SubClass2 extends ParentGeneric {

}

public class GenericGet {

//獲取實際的泛型類型 public static Type findGenericType(Class cls) { Type genType = cls.getGenericSuperclass(); Type finalNeedType = null; if (genType instanceof ParameterizedType) { Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); finalNeedType = params[0]; } return finalNeedType; }

public static void main(String[] args) { SubClass subClass = new SubClass(); SubClass2 subClass2 = new SubClass2(); //打印 subClass 獲取的泛型 System.out.println("subClass: " + findNeedClass(subClass.getClass())); //打印subClass2獲取的泛型 System.out.println("subClass2: " + findGenericType(subClass2.getClass())); } }

//運行這段代碼 打印結果如下 subClass: class java.lang.String subClass2: T

上面代碼:

1、 SubClass 相當于對 ParentGeneric 做了賦值操作 T = String,我們通過反射獲取到了泛型類型為 String

2、SubClass2 對 ParentGeneric沒有做賦值操作 ,我們通過反射獲取到了泛型類型為 T

這里大家肯定會有很多疑問?

1、為啥 1 中沒有傳入任何泛型的信息卻能獲取到泛型類型呢?

2、為啥 2 中我創(chuàng)建對象的時候傳入的泛型是 Integer ,獲取的時候變成了 T 呢?

現(xiàn)在我們來仔細分析一波:

上面我講過,類型擦除其實只是擦除 Code 屬性中的泛型信息,在類常量池屬性中還保留著泛型信息,因此上面的 SubClass 和SubClass2 在編譯的時候其實會保留各自的泛型到字節(jié)碼文件中,一個是 String,一個是 T 。而 subClass 和 subClass2 是運行時動態(tài)創(chuàng)建的,這個時候你即使傳入了泛型類型,也會被擦除掉,因此才會出現(xiàn)上面的結果,到這里,大家是否明了了呢?

如果還有點模糊,我們再來看一個例子:

class ParentGeneric {

}

public class GenericGet { //獲取實際的泛型類型 public static Type findGenericType(Class cls) { Type genType = cls.getGenericSuperclass(); Type finalNeedType = null; if (genType instanceof ParameterizedType) { Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); finalNeedType = params[0]; } return finalNeedType; }

public static void main(String[] args) { ParentGeneric parentGeneric1 = new ParentGeneric(); ParentGeneric parentGeneric2 = new ParentGeneric(){};

//打印 parentGeneric1 獲取的泛型 System.out.println("parentGeneric1: " + findGenericType(parentGeneric1.getClass())); //打印 parentGeneric2 獲取的泛型 System.out.println("parentGeneric2: " + findGenericType(parentGeneric2.getClass()));

} } //運行這段代碼 打印結果如下 parentGeneric1: null parentGeneric2: class java.lang.String

上述代碼 parentGeneric1 和 parentGeneric2 唯一的區(qū)別就是多了 {},獲取的結果卻截然不同,我們在來仔細分析一波:

1、 ParentGeneric 聲明的泛型 T 在編譯的時候其實是保留在了字節(jié)碼文件中,parentGeneric1 是在運行時創(chuàng)建的,由于泛型擦除,我們無法通過反射獲取其中的類型,因此打印了 null

這個地方可能大家又會有個疑問了,你既然保留了泛型類型為 T,那么我獲取的時候應該為 T 才是,為啥打印的結果是 null 呢?

如果你心里有這個疑問,說明你思考的非常細致,要理解這個問題,我們首先要對 Java 類型(Type)系統(tǒng)有一定的了解,這其實和我上面寫的那個獲取泛型類型的方法有關:

//獲取實際的泛型類型 public static Type findGenericType(Class cls) { //獲取當前帶有泛型的父類 Type genType = cls.getGenericSuperclass(); Type finalNeedType = null; //如果當前 genType 是參數(shù)化類型則進入到條件體 if (genType instanceof ParameterizedType) { //獲取參數(shù)類型 <> 里面的那些值,例如 Map 那么就得到 [K,V]的一個數(shù)組 Type[] params = ((ParameterizedType) genType).getActualTypeArguments(); //將第一個泛型類型賦值給 finalNeedType finalNeedType = params[0]; } return finalNeedType; }

上述代碼我們需要先獲取這個類的泛型父類,如果是參數(shù)化類型則進入到條件體,獲取實際的泛型類型并返回。如果不是則直接返回 finalNeedType , 那么這個時候就為 null 了

在例1中:

SubClass1 subClass1 = new SubClass1(); SubClass2 subClass2 = new SubClass2<>(); System.out.println(subClass1.getClass().getGenericSuperclass()); System.out.println(subClass2.getClass().getGenericSuperclass()); //運行程序 打印結果如下 com.dream.java_generic.share.ParentGeneric com.dream.java_generic.share.ParentGeneric

可以看到獲取到了泛型父類,因此會走到條件體里面獲取到實際的泛型類型并返回

在例2中:

ParentGeneric parentGeneric1 = new ParentGeneric(); System.out.println(parentGeneric1.getClass().getGenericSuperclass()); //運行程序 打印結果如下 class java.lang.Object

可以看到獲取到的泛型父類是 Object,因此進不去條件體,所以就返回 null 了

2、parentGeneric2 在創(chuàng)建的時候后面加了 {},這就使得 parentGeneric2 成為了一個匿名內(nèi)部類,且父類就是 ParentGeneric,因為匿名內(nèi)部類是在編譯時創(chuàng)建的,那么在編譯的時候就會創(chuàng)建并攜帶具體的泛型信息,因此 parentGeneric2 可以獲取其中的泛型類型

通過上面兩個例子我們可以得出結論:如果在編譯的時候就保存了泛型類型到字節(jié)碼中,那么在運行時我們就可以通過反射獲取到,如果在運行時傳入實際的泛型類型,這個時候就會被擦除,反射獲取不到當前傳入的泛型實際類型

例子1中我們指定了泛型的實際類型為 String,編譯的時候就將它存儲到了字節(jié)碼文件中,因此我們獲取到了泛型類型。例子2中我們創(chuàng)建了一個匿名內(nèi)部類,同樣在編譯的時候會進行創(chuàng)建并保存了實際的泛型到字節(jié)碼中,因此我們可以獲取到。而 parentGeneric1 是在運行時創(chuàng)建的,雖然 ParentGeneric 聲明的泛型 T 在編譯時也保留在了字節(jié)碼文件中,但是它傳入的實際類型被擦除了,這種泛型也是無法通過反射獲取的,記住上面這條結論,那么對于泛型類型的獲取你就得心應手了

5、泛型獲取經(jīng)驗總結

其實通過上面兩個例子可以發(fā)現(xiàn),當我們定義一個子類繼承一個泛型父類,并給這個泛型一個類型,我們就可以獲取到這個泛型類型

//定義一個子類繼承泛型父類,并給這個泛型一個實際的類型 class SubClass extends ParentGeneric{

}

//匿名內(nèi)部類,其實我們定義的這個匿名內(nèi)部類也是一個子類,它繼承了泛型父類,并給這個泛型一個實際的類型 ParentGeneric parentGeneric2 = new ParentGeneric(){};

因此如果我們想要獲取某個泛型類型,我們可以通過子類的幫助去取出該泛型類型,一種良好的編程實踐就是把當前需要獲取的泛型類用 abstract 聲明

3、邊界

邊界就是在泛型的參數(shù)上設置限制條件,這樣可以強制泛型可以使用的類型,更重要的是可以按照自己的邊界類型來調(diào)用方法

1)、Java 中設置邊界使用 extends 關鍵字,完整語法結構: ,Bound 可以是類和接口,如果不指定邊界,默認邊界為 Object

2)、可以設置多個邊界,中間使用 & 連接,多個邊界中只能有一個邊界是類,且類必須放在最前面,類似這種語法結構

下面我們來演示一下:

abstract class ClassBound{ public abstract void test1(); }

interface InterfaceBound1{ void test2(); }

interface InterfaceBound2{ void test3(); }

class ParentClass { private final T item;

public ParentClass(T item) { this.item = item; }

public void test1(){ item.test1(); }

public void test2(){ item.test2(); }

public void test3(){ item.test3(); } }

class SubClass extends ClassBound implements InterfaceBound1,InterfaceBound2 {

@Override public void test1() { System.out.println(“test1”); }

@Override public void test2() { System.out.println(“test2”); }

@Override public void test3() { System.out.println(“test3”); } }

public class Bound { public static void main(String[] args) { SubClass subClass = new SubClass(); ParentClass parentClass = new ParentClass(subClass); parentClass.test1(); parentClass.test2(); parentClass.test3(); } } //打印結果 test1 test2 test3

4、通配符

1、泛型的協(xié)變,逆變和不變

思考一個問題,代碼如下:

Number number = new Integer(666); ArrayList numberList = new ArrayList();//編譯器報錯 type mismatch

上述代碼,為啥 Number 的對象可以由 Integer 實例化,而 ArrayList 的對象卻不能由 ArrayList 實例化?

要明白上面這個問題,我們首先要明白,什么是泛型的協(xié)變,逆變和不變

1)、泛型協(xié)變,假設我定義了一個 Class 的泛型類,其中 A 是 B 的子類,同時 Class 也是 Class 的子類,那么我們說 Class 在 T 這個泛型上是協(xié)變的

2)、泛型逆變,假設我定義了一個 Class 的泛型類,其中 A 是 B 的子類,同時 Class 也是 Class 的子類,那么我們說 Class 在 T 這個泛型上是逆變的

3)、泛型不變,假設我定義了一個 Class 的泛型類,其中 A 是 B 的子類,同時 Class 和 Class 沒有繼承關系,那么我們說 Class 在 T 這個泛型上是不變的

因此我們可以知道 ArrayList 的對象不能由 ArrayList 實例化是因為 ArrayList 當前的泛型是不變的,我們要解決上面報錯的問題,可以讓 ArrayList 當前的泛型支持協(xié)變,如下:

Number number = new Integer(666); ArrayList numberList = new ArrayList();

2、泛型的上邊界通配符

1)、泛型的上邊界通配符語法結構:,使得泛型支持協(xié)變,它限定的類型是當前上邊界類或者其子類,如果是接口的話就是當前上邊界接口或者實現(xiàn)類,使用上邊界通配符的變量只讀,不可以寫,可以添加 null ,但是沒意義

public class WildCard { public static void main(String[] args) { List integerList = new ArrayList(); List numberList = new ArrayList(); integerList.add(666); numberList.add(123);

getNumberData(integerList); getNumberData(numberList); }

public static void getNumberData(List data) { System.out.println(“Number data :” + data.get(0)); } } //打印結果 Number data: 666 Number data: 123

問題:為啥使用上邊界通配符的變量只讀,而不能寫?

1、,它限定的類型是當前上邊界類或者其子類,它無法確定自己具體的類型,因此編譯器無法驗證類型的安全,所以不能寫

2、假設可以寫,我們向它里面添加若干個子類,然后用一個具體的子類去接收,勢必會造成類型轉換異常

3、泛型的下邊界通配符

1)、泛型的下邊界通配符語法結構:,使得泛型支持逆變,它限定的類型是當前下邊界類或者其父類,如果是接口的話就是當前下邊界接口或者其父接口,使用下邊界通配符的變量只寫,不建議讀

public class WildCard {

public static void main(String[] args) { List numberList = new ArrayList(); List objectList = new ArrayList(); setNumberData(numberList); setNumberData(objectList); }

public static void setNumberData(List data) { Number number = new Integer(666); data.add(number); } }

問題:為啥使用下邊界通配符的變量可以寫,而不建議讀?

1、,它限定的類型是當前下邊界類或者其父類,雖然它也無法確定自己具體的類型,但根據(jù)多態(tài),它能保證自己添加的元素是安全的,因此可以寫

2、獲取值的時候,會返回一個 Object 類型的值,而不能獲取實際類型參數(shù)代表的類型,因此建議不要去讀,如果你實在要去讀也行,但是要注意類型轉換異常

4、泛型的無邊界通配符

1)、無邊界通配符的語法結構:,實際上它等價于 ,也就是說它的上邊界是 Object 或其子類,因此使用無界通配符的變量同樣只讀,不能寫,可以添加 null ,但是沒意義

public class WildCard {

public static void main(String[] args) { List stringList = new ArrayList(); List numberList = new ArrayList(); List integerList = new ArrayList(); stringList.add(“erdai”); numberList.add(666); integerList.add(123); getData(stringList); getData(numberList); getData(integerList); }

public static void getData(List data) { System.out.println("data: " + data.get(0)); } } //打印結果 data: erdai data: 666 data: 123

5、PECS 原則

泛型代碼的設計,應遵循PECS原則(Producer extends Consumer super):

1)、如果只需要獲取元素,使用

2)、如果只需要存儲,使用

//這是 Collections.java 中 copy 方法的源碼 public static void copy(List dest, List src) { //… }

這是一個很經(jīng)典的例子,src 表示原始集合,使用了 ,只能從中讀取元素,dest 表示目標集合,只能往里面寫元素,充分的體現(xiàn)了 PECS 原則

6、使用通配符總結

1)、當你只想讀取值的時候,使用

2)、當你只想寫入值的時候,使用

3)、當你既想讀取值又想寫入值的時候,就不要使用通配符

5、泛型的限制

1)、泛型不能顯式地引用在運行時類型的操作里,如 instanceof 操作和 new 表達式,運行時類型只適用于原生類型

public class GenericLimitedClass { private void test(){ String str = “”; //編譯器不允許這種操作 if(str instanceof T){

} //編譯器不允許這種操作 T t = new T(); } }

2)、不能創(chuàng)建泛型類型的數(shù)組,只可以聲明一個泛型類型的數(shù)組引用

public class GenericLimitedClass { private void test(){ GenericLimitedClass[] genericLimitedClasses; //編譯器不允許 genericLimitedClasses = new GenericLimitedClass[10]; } }

3)、不能聲明類型為泛型的靜態(tài)字段

public class GenericLimitedClass { //編譯器不允許 private static T t; }

4)、泛型類不可以直接或間接地繼承 Throwable

//編譯器不允許 public class GenericLimitedClass extends Throwable {

}

5)、方法中不可以捕獲類型參數(shù)的實例,但是可以在 throws 語句中使用類型參數(shù)

public class GenericLimitedClass { private void test1() throws T{ try {

//編譯器不允許 }catch (T exception){

} } }

6)、一個類不可以重載在類型擦除后有同樣方法簽名的方法

public class GenericLimitedClass { //編譯器不允許 private void test2(List stringList){

}

private void test2(List integerList){

} }

6、問題

1)、類型邊界和通配符邊界有什么區(qū)別?

類型邊界可以有多個,通配符邊界只能有一個

2)、List 和 List 一樣嗎?

不一樣

1、 List 可讀寫,但是 List 只讀

2、List可以有很多子類,但是 List 沒有

二、Kotlin 泛型

Kotlin 泛型和 Java 泛型基本上是一樣的,只不過在 Kotlin 上有些東西換了新的寫法

1、泛型的基本用法

1)、在 Kotlin 中我們定義和使用泛型的方式如下:

//1、定義一個泛型類,在類名后面使用 這種語法結構就是為這個類定義一個泛型 class MyClass{ fun method(params: T) {

} } //泛型調(diào)用 val myClass = MyClass() myClass.method(12)

//2、定義一個泛型方法,在方法名的前面加上 這種語法結構就是為這個方法定義一個泛型 class MyClass{ fun method(params: T){

} } //泛型調(diào)用 val myClass = MyClass() myClass.method(12) //根據(jù) Kotlin 類型推導機制,我們可以把泛型給省略 myClass.method(12)

//3、定義一個泛型接口,在接口名后面加上 這種語法結構就是為這個接口定義一個泛型 interface MyInterface{ fun interfaceMethod(params: T) }

對比 Java 中定義泛型,我們可以發(fā)現(xiàn):在定義類和接口泛型上沒有任何區(qū)別,在定義方法泛型時,Kotlin 是在方法名前面添加泛型,而 Java 是在返回值前面添加泛型

2、邊界

1)、為泛型指定邊界,我們可以使用 這種語法結構,如果不指定泛型的邊界,默認為 Any?

2)、如果有多個邊界,可以使用 where 關鍵字,中間使用 : 隔開,多個邊界中只能有一個邊界是類,且類必須放在最前面

//情況1 單個邊界 class MyClass1 {

var data: T? = null

fun method(params: T) {

} }

//情況2 多個邊界使用 where 關鍵字 open class Animal interface Food interface Food2

class MyClass2 where T : Animal, T : Food, T : Food2 {

fun method(params: T) where T : Animal, T : Food, T : Food2 {

} }

3、泛型實化

泛型實化在 Java 中是不存在的,Kotlin 中之所以能實現(xiàn)泛型實化,是因為使用的內(nèi)聯(lián)函數(shù)會對代碼進行替換,那么在內(nèi)聯(lián)函數(shù)中使用泛型,最終也會使用實際的類型進行替換

1)、使用內(nèi)聯(lián)函數(shù)配合 reified 關鍵字對泛型進行實化,語法結構如下:

inline fun getGenericType() {

}

實操一下:

inline fun getGenericType() = T::class.java

fun main() { //泛型實化 這種情況在 Java 是會被類型擦除的 val result1 = getGenericType() val result2 = getGenericType() println(result1) println(result2) } //打印結果 class java.lang.String class java.lang.Number

2)、實際應用

在我們跳轉 Activity 的時候通常會這么操作

val intent = Intent(mContext,TestActivity::class.java) mContext.startActivity(intent)

有沒有感覺寫這種 TestActivity::class.java 的語法很難受,反正我是覺得很難受,那么這個時候我們就可以使用泛型實化換一種寫法:

//定義一個頂層函數(shù) inline fun startActivity(mContext: Context){ val intent = Intent(mContext,T::class.java) mContext.startActivity(intent) }

//使用的時候 startActivity(mContext)

這種寫法是不是清爽了很多,那么在我們跳轉 Activity 的時候,可能會攜帶一些參數(shù),如下:

val intent = Intent(mContext,TestActivity::class.java) intent.putExtra(“params1”,“erdai”) intent.putExtra(“params2”,“666”) mContext.startActivity(intent)

這個時候我們可以增加一個函數(shù)類型的參數(shù),使用 Lambda 表達式去調(diào)用,如下:

inline fun startActivity(mContext: Context, block: Intent.() -> Unit){ val intent = Intent(mContext,T::class.java) intent.block() mContext.startActivity(intent) }

//使用的時候 startActivity(mContext){ putExtra(“params1”,“erdai”) putExtra(“params2”,“666”) }

4、泛型協(xié)變,逆變和不變

最后

自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進入阿里一直到現(xiàn)在。

深知大多數(shù)初中級Android工程師,想要提升技能,往往是自己摸索成長,自己不成體系的自學效果低效漫長且無助。

因此我收集整理了一份《2024年Android移動開發(fā)全套學習資料》,初衷也很簡單,就是希望能夠幫助到想自學提升又不知道該從何學起的朋友,同時減輕大家的負擔。

既有適合小白學習的零基礎資料,也有適合3年以上經(jīng)驗的小伙伴深入學習提升的進階課程,基本涵蓋了95%以上Android開發(fā)知識點!不論你是剛入門Android開發(fā)的新手,還是希望在技術上不斷提升的資深開發(fā)者,這些資料都將為你打開新的學習之門

如果你覺得這些內(nèi)容對你有幫助,需要這份全套學習資料的朋友可以戳我獲?。?!

由于文件比較大,這里只是將部分目錄截圖出來,每個節(jié)點里面都包含大廠面經(jīng)、學習筆記、源碼講義、實戰(zhàn)項目、講解視頻,并且會持續(xù)更新!

putExtra(“params2”,“666”) }

4、泛型協(xié)變,逆變和不變

最后

自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過,也去過華為、OPPO等大廠,18年進入阿里一直到現(xiàn)在。

深知大多數(shù)初中級Android工程師,想要提升技能,往往是自己摸索成長,自己不成體系的自學效果低效漫長且無助。

因此我收集整理了一份《2024年Android移動開發(fā)全套學習資料》,初衷也很簡單,就是希望能夠幫助到想自學提升又不知道該從何學起的朋友,同時減輕大家的負擔。

[外鏈圖片轉存中…(img-dbFTaLyT-1715792113182)]

[外鏈圖片轉存中…(img-JDdik24g-1715792113184)]

[外鏈圖片轉存中…(img-I552yHSE-1715792113185)]

[外鏈圖片轉存中…(img-Z1l5Wq4S-1715792113186)]

既有適合小白學習的零基礎資料,也有適合3年以上經(jīng)驗的小伙伴深入學習提升的進階課程,基本涵蓋了95%以上Android開發(fā)知識點!不論你是剛入門Android開發(fā)的新手,還是希望在技術上不斷提升的資深開發(fā)者,這些資料都將為你打開新的學習之門

如果你覺得這些內(nèi)容對你有幫助,需要這份全套學習資料的朋友可以戳我獲?。?!

由于文件比較大,這里只是將部分目錄截圖出來,每個節(jié)點里面都包含大廠面經(jīng)、學習筆記、源碼講義、實戰(zhàn)項目、講解視頻,并且會持續(xù)更新!

柚子快報激活碼778899分享:

http://yzkb.51969.com/

相關文章

評論可見,查看隱藏內(nèi)容

本文內(nèi)容根據(jù)網(wǎng)絡資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。

轉載請注明,如有侵權,聯(lián)系刪除。

本文鏈接:http://gantiao.com.cn/post/18975580.html

發(fā)布評論

您暫未設置收款碼

請在主題配置——文章設置里上傳

掃描二維碼手機訪問

文章目錄