# [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
Sign in via Google
Sign in via Facebook
Sign in via X(Twitter)
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
Continue with a different method
New to HackMD?
Sign up
By signing in, you agree to our
terms of service
.