--- title: 'Dagger 依賴注入' disqus: kyleAlien --- Dagger 依賴注入 === ## OverView of Content 如有引用參考請詳註出處,感謝 :smile: 在學習前先 Ioc 之前要先有 **^1^ Java 註解、^2^ Java反射 這兩個基礎**,並且要大致上了解到 6 大基礎的設計模式,尤其是**依賴倒置(Dependence Inversion Principle)** [TOC] ## 何謂依賴 類與類之間的連接,是實體定義,連接固定的實做類方法 ```java= public class Dependency { public static void main(String[] args) { new Car().DriveInfo(); } } class Car { private Gasoline92 g92; Car() { //"1. " g92 = new Gasoline92(); } void DriveInfo() { System.out.println("This car use " + g92.getType()); } } class Gasoline92 { String getType() { return "92"; } } ``` 1. 車直接依賴 92 的汽油,但是如要要使用 95 的汽油就必須在該類內修改,這樣**不符合開閉原則**,若是需要拓展也相當不方便 **--實作--** > ![](https://i.imgur.com/JA1wF1H.png) ### 依賴倒置 * 先提到 OOP 設計模式的六大原則之一,**依賴倒置,高層模組不該依賴低層模組,應該依賴抽象,並且 ==所謂的抽象,是一種相對關係==,並沒有說絕對的抽象** ```java= //1. 抽象 interface Gasoline { String getType(); } class Car { private Gasoline g; Car() { Gasoline = new Gasoline95(); // 2. 調用實體類 } void DriveInfo() { // 呼叫 Gasoline 抽象的相同方法 System.out.println("This car use " + g.getType()); // 不用更改方法 } } // 實作抽象類別 class Gasoline92 implements Gasoline{ @Override public String getType() { return "92"; } } class Gasoline95 implements Gasoline{ @Override public String getType() { return "95"; } } ``` 1. 依賴抽象接口,讓實作的類去實現這個接口 > 如何注入 (Inject) 之後說明 2. 當需求改變唯一需要改動到的就是呼叫實體類(這裡呼叫實體類仍是包含在依賴倒置的範圍內),而呼叫方法的位置則不須做任何更改 :::info * 上層 & 下層模組? ^1^ 上層模塊:呼叫者 ^2^ 下層模塊:被呼叫者 > 依賴倒置: 簡單來說就是要讓 Class 去依賴抽象,而不是依賴實做,這樣對於 **類的擴充來講更加的好** - **抽象**:不須直接實現實際要操作的邏輯,而是定義它的抽象方法,它可以是行為、物品 ... 等等 - **實做**,則是實際實現邏輯 & 數據的地方,它是具體的結果 ::: ### 控制反轉 IoC - 抽象化 * IoC 是 **Inversion of Control**,反轉的就是 ==**對於控制權的反轉**==,**變成依賴注入** DI (Dependency Injection) > 透過注入的類,來決定實作細節 * 將原本的程式需要手動去獲取需要的類,**轉變為透過第三方獲取並使原來的程式被動接收,來達到解偶的效果**,Android 中最常是配合反射一起使用 > ![](https://i.imgur.com/qZyV7la.png) ### 依賴注入 - 注入方式 * 會關心到要如何注入 **抽象** 接口,有三個方法 1. 建構函數 constructor 2. setting interface 3. 該類實做抽象接口 ```java= public class Dependency { public static void main(String[] args) { new Car(new Gasoline95()).DriveInfo(); } } interface Gasoline { String getType(); } // 注入 接口 InjectGas interface InjectGas { void useGas(Gasoline g); } class Car implements InjectGas { private Gasoline g; //"1. " 建構函數注入 Car(Gasoline g) { this.g = g; } //"2. " 方法注入 void setGasline(Gasoline g) { this.g = g; } //"3. " Gasoline#interface 注入 @Override public void useGas(Gasoline g) { this.g = g; } void DriveInfo() { System.out.println("This car use " + g.getType()); } } class Gasoline92 implements Gasoline{ @Override public String getType() { return "92"; } } class Gasoline95 implements Gasoline{ @Override public String getType() { return "95"; } } ``` **--實作--** > ![reference link](https://i.imgur.com/l5ncs8q.png) ## 標準 Java 依賴注入 **JSR-300** 是 Java 的依賴注入標準,(JSR-330,Dependency Injection for Java) ### javax.inject * 由於 inject 是一**個單獨的 Jar 包,並不包含在 JDK 中**,先下載 [**javax.inject**](https://search.maven.org/artifact/javax.inject/javax.inject/1/jar) 的 Jar 包在手動添加到 JDK 中 * 它指定了獲取對象的一種方法,該方法與構造器、工廠以及服務定位這些傳統方法比起來有更好的 **可重用、測試、維護性**,==此方法的處理過程就是依賴注入== > 下面會有範例 * 在查詢依賴的類別時,這個行為稱為 ==解析依賴==,找不到依賴的例項稱為 **不能滿足** ### @Inject & @Provider<T\> * 註解 @Inject 標示 可被注入的 ^1.^ 構造器(construct)、^2.^ 方法(method)、^3.^ 字段(Field),並且可被其他修飾符修飾 (private、protected、public),**注入順序為 構造器 > 字段 > 方法** ```java= @Target({ METHOD, CONSTRUCTOR, FIELD }) @Retention(RUNTIME) @Documented public @interface Inject {} ``` * 註解 @Provider 用來==提供類的實例== (泛型) ### @Qulifier & @Named * 註解 @Qulifier 可以用來區分要使用哪一個實現來提供,它是專門用來註解註解的(註解的註解 :smile:) ```java= @Target(ANNOTATION_TYPE) @Retention(RUNTIME) @Documented public @interface Qualifier {} ``` * 註解 @Named 單例是 Javax 內置的註釋,本身被 @Qulifier 所註釋 ### @Scope & @Singleton * 註解 @Scope 範疇,可以用來時局部、全局單例,**主要看使用者在哪裡建立**,若是重新建立則會失去單例作用(變成了局部單例),**==Scope 本身不具有單例的功能==**,它是專門用來註解註解的 ```java= @Target(ANNOTATION_TYPE) @Retention(RUNTIME) @Documented public @interface Scope {} ``` * 註解 @Singleton 單例是 Javax 內置的註釋,本身被 @Scope 所註釋 ## Dagger2 **Dagger2 是基於 JSR-300 標準 (Java 依賴注入) 的依賴注入框架**,在編譯期間自動生成程式,**負責依賴對象的創建**,Dagger2 是 Dagger1 的分支,之後由 Google 接手進行開發 * 要使用必須在 Gradle 添加依賴 ```groovy= dependencies { ... // 添加 Dagger 依賴 implementation 'com.google.dagger:dagger:2.28.3' annotationProcessor 'com.google.dagger:dagger-compiler:2.28.3' } // 解析中文設置 tasks.withType(JavaCompile) { options.encoding = "UTF-8" } ``` ### dagger 基礎注入 - 無參 constructor | 註釋 | 功能 | | -------- | -------- | | @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 | | @Component | 接口、橋樑,連接依賴 & 提供依賴方的 interface | * Inject 標記是屬於 javax 包,也就是 dagger 是包裝了 java,以下是該註解的源碼,**運行在 Runtime**,並可以註釋在 ^1^ 方法 ^2^ 建構函數 ^3^ 參數 ```java= @Target({ METHOD, CONSTRUCTOR, FIELD }) @Retention(RUNTIME) @Documented public @interface Inject {} ``` * 範例:Car 依賴於 Engine,所以在 Engine(依賴,也就是需要 **被注入**)上標示 @Inject ```java= /** * Car 依賴需求方,依賴了 Engine 類 */ public class Car { /** * 1. 標記需要依賴的元素 * 2. 標記構造函數 */ @Inject // -> 它就會尋找到相同標記的 Engine construct 並創建物件 Engine mEngine; // 不可為私有 public Car() { DaggerICarComponent.builder().build().inject(this); } public Engine getEngine() { return mEngine; } public static void main(String ... args){ Car car = new Car(); car.getEngine().start(); } } /** * 依賴提供 & 依賴需求方的橋樑 */ @Component public interface ICarComponent { void inject(Car car); } /** * 提供依賴方 */ public class Engine { //標記 - 提供依賴的構造函數 @Inject Engine() { } public void start() { System.out.println("Engine start run ~~"); } } ``` **--實做結果--** > ![](https://i.imgur.com/qBeX7lK.png) :::warning * Warning of inject 1. 只使用 `@Inject` & `@Component` 兩個註釋的話,提供依賴方的建構函數只能給予無參構造函數,**若是 inject 注入到有參數的 construct 則會錯誤** 2. 提供類中只能一個 inject 註釋在 construct 上 > 否則它會不確定要使用哪個建構函數注入 ::: ### 有參 or 第三方庫注入 1. 有參構造函數:上面最後提到的 warning 中有說到 inject 只能注入無參 construct 這在使用上相當不方便,所以這邊有提出另外兩個註釋 ^1^ Module、^2^ Provides | 註釋 | 功能 | | -------- | -------- | | @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 | | @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 @Module** | | - | - | | @Module | 提供依賴方,**標示它是一個提供工廠** | | @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**==Provides 是 ++具體提供++ 的產線==** | :::warning Component 可以指定多個 Module,在指定後透過 dagger 就會產生像對的程式 ::: ```java= /** * 未何需要 Module 註釋? * 1. 很多時候我們需要提供依賴的構造函數是第三方庫,我們沒辦法在第三方庫上的 construct 加上 @Inject 註釋, * 而 CarModule 就作為一層隔離使用第三方庫 * 2. 提供的構造函數是有參數的,那就無法使用 @Inject 註釋構造參數 * 3. 需要注入的成員對象是抽象,則 @Inject 也無法使用 */ @Module // 間接專用的工廠 public class CarModule { public CarModule() { } /** * provider 用於 Module 被標注的類中,只能標住在方法之上 * 該方法在需要提供依賴時被調用 * 它會注入被 @Inject 註釋的 Member */ @Provides // 可當作間接標注 Engine provideEngine() { return new Engine("Android Dagger Engine"); } } /** * 依賴提供 & 依賴需求方的橋樑 */ @Component(modules = {CarModule.class}) // 指定特定間接 Module 工廠 //@Component public interface ICarComponent { void inject(Car car); } ``` :::danger * 注入類必須是實體類,不可以是抽象類 ```java= public interface ICarComponent { void inject(Car car); } ``` ::: 2. 第三方 Library:由於我們不可能去改動到 depend 的第三方 Library(除非整個專案拉下來重作,但這並不符合效益),所以我們必須多一個中間層 ^1.^ 提供另一個 Module 工廠 ^2.^ 規定 Component 橋樑指定 Module(提供依賴方) ```java= public class MyGson { @Inject Gson mGson; // dagger 無法知道我們實際上有使用的類是哪個類 } ``` 3. 抽象依賴:我們很常會使用到 OOP 的依賴倒置概念,但是 dagger 無法注入上層抽象(因為抽象不能創建,它也無法知道我們實際上有使用的類是哪個類) ```java= public abstract class Engine { public abstract void start(); } public class Car { @Inject Engine mAndroidEngine; // dagger 無法知道我們實際上有使用的類是哪個類 } ``` --- * 依賴方的 inject 相同,但 Engine 並不會直接提供依賴,而是透過 Module + Provider(CarModule 類)提供依賴(以下為完整程式) ```java= /** * 不直接提供依賴 */ public class Engine { private String mBrand; // @Inject // Err: 無法注入有參構造函數 Engine(String brand) { mBrand = brand; } public void start() { if(mBrand != null) { System.out.println("Engine brand: " + mBrand); } System.out.println("Engine start run ~~"); } } /** * 間接提供依賴給 Car,真正提供的地方 */ public class CarModule { public CarModule() { } /** * provider 用於 Module 被標注的類中,只能標住在方法之上 * 該方法在需要提供依賴時被調用 * 它會注入被 @Inject 註釋的 Member */ @Provides // 可當作間接標注 Engine provideEngine() { return new Engine("Android Dagger Engine"); } } /** * 依賴提供 & 依賴需求方的橋樑 */ @Component(modules = {CarModule.class}) // 指定特定間接 Module 工廠 //@Component public interface ICarComponent { void inject(Car car); } /** * Car 依賴需求方,依賴了 Engine 類 */ public class Car { @Inject // -> 它就會尋找到相同標記的 Engine construct 並創建物件 Engine mEngine; public Car() { DaggerICarComponent.builder() .build() .inject(this); } public Engine getEngine() { return mEngine; } public static void main(String ... args){ Car car = new Car(); car.getEngine().start(); } } ``` **--實做結果--** > ![](https://i.imgur.com/efOnkQr.png) ### 指定 Member 使用的對象 * 以下有 2 個狀況 1. 多個需要被注入類:若是依賴需求方有多個相同的對象,一般來說 dagger 會依照類型來做注入的動作,但是 **若要使用指定就必須使用 @Quialifier 註解** ```java= // 以下在 car 中有多個相同類型的 Member,但是各自要求使用不同的 Engine,dagger 是無法區分的 @Inject Engine mEngine; @Inject Engine mEngineWhite; @Inject Engine mEngineBlue; ``` 2. 多個提供工廠:若是在 Module 中有多個提供依賴的 Provider 產線,dagger 就會分不清楚要使用哪個 Provider,最終導致編譯錯誤 ```java= @Module public class CarModule { public CarModule() { } @Provides Engine provideEngine1() { return new Engine("Android Dagger 1 Engine"); } @Provides Engine provideEngine2() { return new Engine("Android Dagger 2 Engine"); } } ``` **--錯誤情況--** > @Inject 被多次注入 > > ![](https://i.imgur.com/cKenzVd.png) * 這時就可以使用到 Qualifier 註解,**Qualifier 主要是註解在註解之上,^1^首先創建你所需要的註解(假設為註解 A),並且標記該註解為 RUNTIME,^2^提供依賴方加上註解 A,^3^在依賴方加上相同的註解 A** | 註釋 | 功能 | | -------- | -------- | | @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 | | @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 Module** | | - | - | | @Module | 提供依賴方,**標示它是一個提供工廠** | | @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**Provides 是 ++具體提供++ 的產線** | | - | - | | @Qualifier | 屬於 Javax 基礎注入,==**註解上的註解**==,用來區分注入成員 | | @Named | 屬於 Javax 基礎注入,Javax 所提供的內置 String 型態註解 | ```java= // 1. 創建註解 @Qualifier // 註解上的註解 @Retention(RetentionPolicy.RUNTIME) public @interface EngineColor { Color color() default Color.BLACK; enum Color { RED, WHITE, BLUE, BLACK, TAN } } // 2. 提供方 @Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) @Provides Engine provideEngineWhite() { return new Engine("Android Engine of White !!!"); } // 3. 需求方,同時加上限制 @Inject // 一般來說要注入哪個 Member 是透過該 Member 的類型決定 @Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應 Engine mEngineWhite; ``` :::success * @Named 就是被 @Qualifier 註釋 ```java= @Qualifier @Documented @Retention(RUNTIME) public @interface Named { /** The name. */ String value() default ""; } ``` ::: * 以下為 @Inject + @Providers + @Qualifier 完整範例程式 ```java= /** * Car 依賴需求方,依賴了 Engine 類 */ public class Car { /** * @Inject 有兩個功能 * 1. 標記需要依賴的元素 * 2. 標記構造函數 * Dagger 它就會尋找到相同標記的 Engine construct 並創建物件 */ @Inject Engine mEngine; @Inject // 一般來說要注入哪個 Member 是透過該 Member 的類型決定 @Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應 Engine mEngineWhite; @Inject // 這邊有兩個相同類型(都是 Engine)的 Member 則 Dagger 無法確定要注入哪個 @Engine.EngineColor(color = Engine.EngineColor.Color.BLUE) // 自訂註解讓 Dagger 分析,要與 CarModule 對應 Engine mEngineBlue; @Inject @Named("Named") // 使用 String 分類 Engine mEngineNamed; public Car() { DaggerICarComponent.builder() // .carModule(new CarModule()) // 可以不用 /** * public ICarComponent build() { * if (carModule == null) { * this.carModule = new CarModule(); // 會自行創建 * } * return new DaggerICarComponent(carModule); * } */ .build() .inject(this); } public Engine getEngine() { return mEngine; } public Engine getWhiteEngine() { return mEngineWhite; } public Engine getBlueEngine() { return mEngineBlue; } public Engine getBlueNamed() { return mEngineNamed; } public static void main(String ... args){ Car car = new Car(); car.getEngine().start(); car.getWhiteEngine().start(); car.getBlueEngine().start(); car.getBlueNamed().start(); } } /** * 在提供方的 Module 加上相對的註解 */ @Module public class CarModule { public CarModule() { } /** * provider 用於 Module 被標注的類中,只能標住在方法之上 * 該方法在需要提供依賴時被調用 * 它會注入被 @Inject 註釋的 Member */ @Provides Engine provideEngine() { return new Engine("Android Dagger Engine"); } @Engine.EngineColor(color = Engine.EngineColor.Color.WHITE) @Provides Engine provideEngineWhite() { return new Engine("Android Engine of White !!!"); } @Engine.EngineColor(color = Engine.EngineColor.Color.BLUE) @Provides Engine provideEngineBlue() { return new Engine("Android Engine of Blue !!!"); } @Engine.EngineColor(color = Engine.EngineColor.Color.RED) @Provides Engine provideEngineRed() { return new Engine("Android Engine of RED !!!"); } @Named("Named") @Provides Engine provideEngineNamed() { return new Engine("Android Engine of Named ???"); } } /** * 自訂需要註解,並使用 Qualifier 註釋 */ public class Engine { @Qualifier // 註解上的註解 @Retention(RetentionPolicy.RUNTIME) public @interface EngineColor { Color color() default Color.BLACK; enum Color { RED, WHITE, BLUE, BLACK, TAN } } private String mBrand; //標記 construct (也可以標記 method, field) @Inject // 一個類只能有一個 @Inject Engine() { } // @Inject // Err: 無法注入有參構造函數 Engine(String brand) { mBrand = brand; } public void start() { if(mBrand != null) { System.out.println("Engine brand: " + mBrand); } System.out.println("Engine start run ~~"); } } /** * 依賴提供 & 依賴需求方的橋樑 */ @Component(modules = {CarModule.class}) public interface ICarComponent { void inject(Car car); } ``` **--實做結果--** > ![](https://i.imgur.com/xAsIoTh.png) ### 範疇 @Scope * 聽到 Scope 有學習過 C++ 的就會比較熟悉,其實 **@Scope 的意義範圍,在 dagger 中就是對注入的對象的限定,若是相同範圍的就使用同一個對象(也就是單例)** | 註釋 | 功能 | | -------- | -------- | | @Inject | 依賴、提供依賴方,屬於 Java 基礎注入 | | @Component | 接口、橋樑,連接依賴&提供依賴方,**可以指定 Module** | | - | - | | @Module | 提供依賴方,**標示它是一個提供工廠** | | @Provides | 必須配合 Module 使用 **只能註釋在方法上**,Module 標示完工廠後,**Provides 是 ++具體提供++ 的產線** | | - | - | | @Qualifier | 屬於 Javax 基礎注入,**註解上的註解**,用來區分注入成員 | | @Named | 屬於 Javax 基礎注入,Javax 所提供的內置 String 型態註解 | | - | - | | @Scope | 屬於 Javax 基礎注入,==**註解上的註解**==,可以實現出單例,包括局部、全局單例(一次 Dagger 就是一個對象),看使用者如何使用 | | @Singleton | 屬於 Javax 基礎注入,Javax 所提供的內置 | ```java= // 1. 實現自己的註釋 @Scope // 1. 定義局部單例限定,++Scope 本身不具有單例功能++, // 每次注入都會新的對向,但對於單次注入就是單例 @Retention(RetentionPolicy.RUNTIME) public @interface CarScope { } // 2. 註釋依賴提供方 @Engine.CarScope // 提供限定單例的實體 @Provides public Engine provideEngine() { return new Engine("Alien Brand"); } // 3. Component 也需要註釋,代表提供的是單例對象 @Engine.CarScope // 提供限定單例的抽象 @Component(modules = {CarModule.class}) // 提供者 CarModule.class public interface ICarComponent { void injectEngine(Car car); } ``` :::success * @Singleton 就是被 @Scope 注入(@Scope 註解本身不具有單例功能) ```java= @Scope @Documented @Retention(RUNTIME) public @interface Singleton {} ``` ::: * 以下是完整程式 (使用 @Scope,而 @Singleton 只差異在不用自己定義註解) ```java= /** * 實際工廠 */ public class Engine { /** * Scope 是註解上的註解 * 可以定義 "限定註解作用域" -> 實現局部單例 (有備該註解註解的才可以使用) */ @Scope // 1. 定義局部單例限定,Scope 本身不具有單例功能,每次注入都會新的對向,但對於單次注入就是單例 @Retention(RetentionPolicy.RUNTIME) public @interface CarScope { } private String mBrand; public Engine(String brand) { mBrand = brand; } public void start() { System.out.println(mBrand + ", Android Dagger Engine Running~ ~"); } } /** * 間接工廠 */ @Module public class CarModule { /** * 2. 標記提供依賴方 * Singleton 只差異在不用自己定義註解 */ @Engine.CarScope // 提供限定單例的實體 @Provides public Engine provideEngine() { return new Engine("Alien Brand"); } } /** * 連接雙方的 Component 也需要註解 */ @Engine.CarScope // 提供限定單例的抽象 @Component(modules = {CarModule.class}) public interface ICarComponent { void injectEngine(Car car); } public class Car { @Inject Engine mEngineApple; @Inject Engine mEngineBanana; public Car() { // 這是一次的單例,若是在重新呼叫一次 build 則會是不同對象 !!! (因為會有新的對象) DaggerICarComponent.builder() .build() .injectEngine(this); } public Engine getEngineApple() { // 取到同一個對象 System.out.println("mEngineApple: " + mEngineApple.hashCode()); return mEngineApple; } public Engine getEngineBanana() { // 取到同一個對象 System.out.println("mEngineBanana: " + mEngineBanana.hashCode()); return mEngineBanana; } public static void main(String[] args) { Car car = new Car(); car.getEngineApple().start(); car.getEngineBanana().start(); } } ``` **--實做結果--** > ![](https://i.imgur.com/R9KYOea.png) ### @Component 依賴 * **@Component 有一個方法是 dependencies**,它有點類似於一種註解的繼承(或是應該稱為依賴) 1. 先創建一個小的 Dagger 注入關係 (汽油),提供實體需要的 Gasoline 依賴 ```java= // 實體提供 public class Gasoline { private int mType; public Gasoline(int type) { mType = type; } public void showGasoline() { String gasoline; if(mType == 0) { gasoline = "Gasoline 95"; } else { gasoline = "Gasoline 92"; } System.out.println(gasoline); } } // 代理提供 @Module public class GasolineModule { @Provides public Gasoline gasolineProvider() { return new Gasoline(1); } } ``` 2. 創建較大的 Dagger 依賴關係 (引擎),提供實體需要的 Engine 依賴 ```java= // 實體提供 public class Engine { private String mBrand; public Engine(String brand) { mBrand = brand; } public void start() { System.out.println(mBrand + ", Android Dagger Engine Running~ ~"); } } // 代理提供 @Module public class EngineModule { @Provides public Engine engineProvider() { return new Engine("Alien"); } } ``` 3. Component 連結關係,並添加 dependencies 依賴關係 ```java= @Component( modules = {EngineModule.class}, dependencies = {IGasolineComponent.class}) // 依賴有點像是繼承、依賴關係 public interface ICarComponent { void injectEngine(Car car); } ``` 4. 使用者就可以透過注入 Engine 類,並在內部再次注入 Gasoline 類 ```java= public class Car { @Inject Engine mEngine; @Inject Gasoline mGasoline; public Car() { // 成員注入 DaggerICarComponent.builder() .iGasolineComponent(DaggerIGasolineComponent.builder().build()) // 沒注入就不可以使用 .build() .injectEngine(this); } public Engine getEngine() { return mEngine; } public Gasoline getGasoline() { return mGasoline; } public static void main(String[] args) { Car car = new Car(); car.getGasoline().showGasoline(); car.getEngine().start(); } } ``` **--實做結果--** > ![](https://i.imgur.com/73CAUrR.png) ### Lazy 加載延遲 * 有時候可以使用加載延遲 Lazy<T\> 泛型類,這樣就可以省記憶體空間,在需要的時候再去獲取 ```java= // 以下就不提供 @Moduel @Component 程式,基本上與上面相同 public class Classmate { @Inject Lazy<Info> info; public Classmate() { DaggerIInfoComponent.builder() .build() .inject(this); } public Info getInfo() { // 在需要時才取得數據 return info.get(); } public static void main(String[] args) { System.out.println(new Classmate().getInfo()); } } ``` **--實做結果--** > ![](https://i.imgur.com/BVeElbd.png) ## Appendix & FAQ :::info ::: ###### tags: `Android 進階` `Java 基礎進階`