# [Java] 型別擦除 Type erasure 型別擦除是 Java 泛型(Generics)設計時的一種核心機制。 Java 為了箱溶於舊版(1.4之前)的程式,在編譯後會把泛型的型別資訊擦除,只保留原本的類型,真正運行石臼不再有泛型資訊存在。 ## 先來舉例 ```java List<String> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); ``` 我們都知道 Java 的運行機制是基於類別(Class)的,這兩行程式碼在編譯後會被轉換成: ```java List list1 = new ArrayList(); List list2 = new ArrayList(); ``` 也就是說 `List<String>` 和 `List<Integer>` 在編譯後都變成了 `List`,這就是型別擦除的結果。 實際上在 JVM 中運行時,這兩個 `List` 物件並沒有任何型別資訊,它們都是 `List` 類型。 ## 為什麼要這樣設計? - 向下相容性:Java 泛型是在 Java 5 引入的,為了讓舊版程式碼能夠與新版本兼容,必須在編譯時擦除型別資訊。 - 型別檢查發生在"編譯期",運行期不會有型別安全的 overhead。 ## 型別擦除對我有什麼影響? 1. 不能直接判斷泛型參數的型別 ```java List<String> list1 = new ArrayList<>(); if (list1 instanceof List<String>) { // 編譯錯誤 // ... } ``` 而應該要寫成 ```java List<String> list1 = new ArrayList<>(); if (list1 instanceof List) { // ... } 2. 無法建立泛型陣列 ```java List<String>[] arr = new ArrayList<String>[10]; // 編譯錯誤 ``` 3. 強制轉型警告 因為型別資訊會被擦除,Java 在某些情況下會強制你轉型,但無法保證型別安全 ## @SuppressWarnings("unchecked") 由於型別擦除,有些地方需要「強制轉型」(unchecked cast),但編譯器會發出「Unchecked cast」警告。 這時可以加上 @SuppressWarnings("unchecked") 來壓制這類警告。 ```java @SuppressWarnings("unchecked") public <T> List<T> getList() { return (List<T>) new ArrayList<>(); } ``` 這裡因為無法確定 `List<T>` 的實際型別,所以需要使用 `@SuppressWarnings("unchecked")` 來告訴編譯器這是有意為之的。 ## 實務應用 1. 泛型 Repository 假設我們用 Spring data JPA,常會寫一個抽象的 Repository 類別,裡面使用泛型來處理不同的實體類別。 ```java public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {} ``` 若我們想要寫一個自訂共用方法 ```java public class MyRepositoryImpl<T, ID> implements MyRepository<T, ID> { @Override @SuppressWarnings("unchecked") public List<T> findByRawSql(String sql) { // 假設 jdbcTemplate 查詢返回 List<Map<String, Object>> List<Map<String, Object>> rows = jdbcTemplate.queryForList(sql); List<T> result = new ArrayList<>(); for (Map<String, Object> row : rows) { // 這裡通常會用反射或手動轉型 result.add((T) mapToEntity(row)); // 這裡就是 unchecked cast } return result; } } ``` 這邊我們就會使用 `@SuppressWarnings("unchecked")` 來壓制編譯器的警告。 因為 mapToEntity(row) 回傳的是 Object,你只能強轉型成 T,但 Java 編譯器無法驗證這個轉型是否正確,所以會出現 unchecked cast 警告。 這就是型別擦除的影響:無法在運行時檢查泛型型別,只能信任程式設計師。 2. EventListener 泛型應用 Spring 事件機制也常遇到泛型擦除問題 ```java @EventListener public <T> void handleMyEvent(MyEvent<T> event) { T data = event.getData(); // 無法判斷 T 的具體型別 } ``` 假如你要把這個 event 轉型給另一個 service 處理: ```java @SuppressWarnings("unchecked") public void publishEvent(Object obj) { MyEvent<String> event = (MyEvent<String>) obj; applicationContext.publishEvent(event); } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up