<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      Java泛型符號T、E、K、V、? 傻傻分不清楚

      前言

      今天想和大家聊聊Java泛型中那些讓人眼花繚亂的符號——T、E、K、V、?。

      有些小伙伴在工作中,可能經(jīng)常遇到這樣的場景:閱讀框架源碼時被各種泛型符號繞暈,寫業(yè)務代碼時不確定該用哪個符號,或者面試時被問到泛型通配符的區(qū)別一頭霧水。

      其實,這些符號并沒有那么神秘,只要理解了它們的設計初衷和使用場景,就能輕松掌握。

      今天,我就從淺入深,帶你徹底搞懂Java泛型的這些符號,希望對你會有所幫助。

      為什么需要泛型符號?

      在深入具體符號之前,我們先聊聊為什么Java要引入泛型,以及為什么需要這些符號。

      泛型的前世今生

      在Java 5之前,集合類只能存儲Object類型,這導致兩個問題:

      1. 類型不安全:任何對象都能放進集合,取出時需要強制類型轉(zhuǎn)換
      2. 運行時異常:類型轉(zhuǎn)換錯誤只能在運行時發(fā)現(xiàn)
      // Java 5之前的寫法 - 容易出錯
      List list = new ArrayList();
      list.add("hello");
      list.add(123); // 編譯通過,但邏輯錯誤
      
      String str = (String) list.get(1); // 運行時ClassCastException!
      

      泛型的引入解決了這些問題,讓類型錯誤在編譯期就能被發(fā)現(xiàn)。

      符號的作用

      泛型符號本質(zhì)上是一種類型參數(shù),它們讓代碼:

      • 更安全:編譯期類型檢查
      • 更清晰:代碼自文檔化
      • 更靈活:支持代碼復用

      有些小伙伴在工作中可能覺得這些符號很抽象,其實它們就像數(shù)學中的變量x、y、z一樣,是占位符而已。

      接下來,讓我們逐個揭開它們的神秘面紗。

      符號T:最通用的類型參數(shù)

      T是Type的縮寫,這是最常用、最通用的泛型符號。當你不確定用什么符號時,用T通常不會錯。

      為什么需要T?

      T代表"某種類型",它讓類、接口、方法能夠處理多種數(shù)據(jù)類型,同時保持類型安全。

      示例代碼

      // 1. 泛型類 - 包裝器
      public class Box<T> {
          private T value;
          
          public Box(T value) {
              this.value = value;
          }
          
          public T getValue() {
              return value;
          }
          
          public void setValue(T value) {
              this.value = value;
          }
          
          // 2. 泛型方法
          public <T> void printArray(T[] array) {
              for (T element : array) {
                  System.out.println(element);
              }
          }
      }
      
      // 使用示例
      public class TExample {
          public static void main(String[] args) {
              // 字符串類型的Box
              Box<String> stringBox = new Box<>("Hello");
              String value1 = stringBox.getValue(); // 不需要強制轉(zhuǎn)換
              
              // 整數(shù)類型的Box  
              Box<Integer> intBox = new Box<>(123);
              Integer value2 = intBox.getValue(); // 類型安全
              
              // 泛型方法使用
              stringBox.printArray(new String[]{"A", "B", "C"});
              intBox.printArray(new Integer[]{1, 2, 3});
          }
      }
      

      深度剖析

      有些小伙伴在工作中可能會困惑:類上的<T>和方法上的<T>是同一個T嗎?

      答案是不一定

      它們屬于不同的作用域:

      public class ScopeExample<T> {          // 類級別T
          private T field;                    // 使用類級別T
          
          public <T> void method(T param) {   // 方法級別T - 隱藏類級別T!
              System.out.println("類T: " + field.getClass());
              System.out.println("方法T: " + param.getClass());
          }
      }
      
      // 測試
      ScopeExample<String> example = new ScopeExample<>();
      example.method(123); 
      // 輸出:
      // 類T: class java.lang.String
      // 方法T: class java.lang.Integer
      

      為了避免混淆,建議使用不同的符號:

      public class ClearScopeExample<T> {          // 類級別T
          public <U> void method(U param) {        // 方法級別U
              // 清晰區(qū)分
          }
      }
      

      為了更直觀理解泛型類的工作原理,我畫了一個類圖:

      image

      泛型類的好處:

      image

      使用場景

      • 通用的工具類、包裝類
      • 不確定具體類型但需要類型安全的場景
      • 框架基礎組件

      符號E:集合元素的專屬代表

      E是Element的縮寫,主要用于集合框架中表示元素類型。

      雖然功能上E和T沒有區(qū)別,但使用E能讓代碼意圖更清晰。

      為什么需要E?

      在集合上下文中,E明確表示"元素類型",讓代碼更易讀,符合最小驚訝原則。

      示例代碼

      // 自定義集合接口
      public interface MyCollection<E> {
          boolean add(E element);
          boolean remove(E element);
          boolean contains(E element);
          Iterator<E> iterator();
      }
      
      // 自定義ArrayList實現(xiàn)
      public class MyArrayList<E> implements MyCollection<E> {
          private Object[] elements;
          private int size;
          
          public MyArrayList() {
              this.elements = new Object[10];
              this.size = 0;
          }
          
          @Override
          public boolean add(E element) {
              if (size >= elements.length) {
                  // 擴容邏輯
                  Object[] newElements = new Object[elements.length * 2];
                  System.arraycopy(elements, 0, newElements, 0, elements.length);
                  elements = newElements;
              }
              elements[size++] = element;
              return true;
          }
          
          @Override
          @SuppressWarnings("unchecked")
          public E get(int index) {
              if (index < 0 || index >= size) {
                  throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
              }
              return (E) elements[index]; // 注意:這里需要強制轉(zhuǎn)換
          }
          
          // 其他方法實現(xiàn)...
      }
      
      // 使用示例
      public class EExample {
          public static void main(String[] args) {
              MyCollection<String> stringList = new MyArrayList<>();
              stringList.add("Java");
              stringList.add("泛型");
              
              // 編譯期類型檢查
              // stringList.add(123); // 編譯錯誤!
              
              MyCollection<Integer> intList = new MyArrayList<>();
              intList.add(1);
              intList.add(2);
          }
      }
      

      深度剖析

      有些小伙伴在工作中可能會問:為什么Java集合框架選擇E而不是T?

      這背后體現(xiàn)了領域驅(qū)動設計的思想:

      • List<E>:E明確表示列表中的元素
      • Set<E>:E明確表示集合中的元素
      • Collection<E>:E明確表示集合元素

      這種命名讓API更加自文檔化。看到List<String>,我們立即知道這是字符串列表;而如果寫成List<T>,含義就不那么清晰了。

      類型擦除的真相
      雖然我們在代碼中寫了MyArrayList<String>,但在運行時,JVM看到的是MyArrayList(原始類型)。

      泛型信息被擦除了,這就是為什么在get方法中需要強制轉(zhuǎn)換的原因。

      為了理解集合框架中E的使用,我畫了一個繼承關系圖:

      image

      使用場景

      • 自定義集合類
      • 處理元素類型的工具方法
      • 任何需要明確表示"元素"的場景

      符號K和V:鍵值對的黃金搭檔

      K和V分別代表Key和Value,是專門為Map等鍵值對數(shù)據(jù)結構設計的。

      它們總是成對出現(xiàn)。

      為什么需要K和V?

      在Map上下文中,明確區(qū)分鍵類型和值類型非常重要,K和V讓這種區(qū)分一目了然。

      示例代碼

      // 自定義Map接口
      public interface MyMap<K, V> {
          V put(K key, V value);
          V get(K key);
          boolean containsKey(K key);
          Set<K> keySet();
          Collection<V> values();
          Set<Entry<K, V>> entrySet();
          
          interface Entry<K, V> {
              K getKey();
              V getValue();
              V setValue(V value);
          }
      }
      
      // 自定義HashMap實現(xiàn)
      public class MyHashMap<K, V> implements MyMap<K, V> {
          private static final int DEFAULT_CAPACITY = 16;
          private Node<K, V>[] table;
          private int size;
          
          // 鏈表節(jié)點
          static class Node<K, V> implements MyMap.Entry<K, V> {
              final K key;
              V value;
              Node<K, V> next;
              
              Node(K key, V value, Node<K, V> next) {
                  this.key = key;
                  this.value = value;
                  this.next = next;
              }
              
              @Override
              public K getKey() {
                  return key;
              }
              
              @Override
              public V getValue() {
                  return value;
              }
              
              @Override
              public V setValue(V newValue) {
                  V oldValue = value;
                  value = newValue;
                  return oldValue;
              }
          }
          
          public MyHashMap() {
              table = new Node[DEFAULT_CAPACITY];
              size = 0;
          }
          
          @Override
          public V put(K key, V value) {
              int index = hash(key) & (table.length - 1);
              Node<K, V> head = table[index];
              
              // 檢查是否已存在相同key
              for (Node<K, V> node = head; node != null; node = node.next) {
                  if (key.equals(node.key)) {
                      V oldValue = node.value;
                      node.value = value;
                      return oldValue;
                  }
              }
              
              // 新節(jié)點插入鏈表頭部
              table[index] = new Node<>(key, value, head);
              size++;
              return null;
          }
          
          @Override
          public V get(K key) {
              int index = hash(key) & (table.length - 1);
              for (Node<K, V> node = table[index]; node != null; node = node.next) {
                  if (key.equals(node.key)) {
                      return node.value;
                  }
              }
              return null;
          }
          
          private int hash(K key) {
              return key == null ? 0 : key.hashCode();
          }
          
          // 其他方法實現(xiàn)...
      }
      
      // 使用示例
      public class KVExample {
          public static void main(String[] args) {
              MyMap<String, Integer> ageMap = new MyHashMap<>();
              ageMap.put("張三", 25);
              ageMap.put("李四", 30);
              
              // 類型安全
              Integer age = ageMap.get("張三"); // 不需要強制轉(zhuǎn)換
              // ageMap.put(123, "test"); // 編譯錯誤!
              
              // 遍歷示例
              for (String name : ageMap.keySet()) {
                  Integer personAge = ageMap.get(name);
                  System.out.println(name + ": " + personAge);
              }
          }
      }
      

      深度剖析

      有些小伙伴在工作中可能會發(fā)現(xiàn),K和V不僅用于Map,還廣泛用于各種鍵值對場景:

      // 元組(Tuple) - 包含兩個不同類型的對象
      public class Pair<K, V> {
          private final K key;
          private final V value;
          
          public Pair(K key, V value) {
              this.key = key;
              this.value = value;
          }
          
          public K getKey() { return key; }
          public V getValue() { return value; }
      }
      
      // 使用
      Pair<String, Integer> nameAge = new Pair<>("王五", 28);
      String name = nameAge.getKey();
      Integer age = nameAge.getValue();
      

      K的約束
      在大多數(shù)情況下,K應該是不變的對象(或者有正確的hashCode和equals實現(xiàn)),因為Map依賴這些方法來定位值。

      為了理解Map中K和V的關系,我畫了一個存儲結構圖:
      image

      使用場景

      • Map及其相關實現(xiàn)
      • 鍵值對數(shù)據(jù)結構
      • 需要返回多個相關值的場景
      • 緩存實現(xiàn)

      符號?:通配符的魔法

      ?是泛型中最靈活也最容易讓人困惑的符號,它代表"未知類型"。

      為什么需要??

      有些時候,我們不需要知道具體類型,只需要表達"某種類型"的概念,這時候?就派上用場了。

      示例代碼

      import java.util.ArrayList;
      import java.util.List;
      
      public class WildcardExample {
          
          // 1. 無界通配符 - 接受任何類型的List
          public static void printList(List<?> list) {
              for (Object element : list) {
                  System.out.println(element);
              }
          }
          
          // 2. 上界通配符 - 只接受Number及其子類的List
          public static double sumOfList(List<? extends Number> list) {
              double sum = 0.0;
              for (Number number : list) {
                  sum += number.doubleValue();
              }
              return sum;
          }
          
          // 3. 下界通配符 - 只接受Integer及其父類的List
          public static void addNumbers(List<? super Integer> list) {
              for (int i = 1; i <= 5; i++) {
                  list.add(i); // 可以添加Integer,因為Integer是?的子類
              }
          }
          
          // 4. 通配符在類定義中的使用
          public static class BoxHandler {
              // 只能從box中讀取,不能寫入
              public static void readFromBox(Box<?> box) {
                  Object value = box.getValue();
                  System.out.println("Read: " + value);
              }
              
              // 只能向box中寫入,不能讀取(除了Object)
              public static void writeToBox(Box<? super String> box, String value) {
                  box.setValue(value);
              }
          }
          
          public static void main(String[] args) {
              // 無界通配符使用
              List<String> stringList = List.of("A", "B", "C");
              List<Integer> intList = List.of(1, 2, 3);
              printList(stringList); // 可以接受
              printList(intList);    // 也可以接受
              
              // 上界通配符使用
              List<Integer> integers = List.of(1, 2, 3);
              List<Double> doubles = List.of(1.1, 2.2, 3.3);
              System.out.println("Int sum: " + sumOfList(integers)); // 6.0
              System.out.println("Double sum: " + sumOfList(doubles)); // 6.6
              
              // 下界通配符使用
              List<Number> numberList = new ArrayList<>();
              addNumbers(numberList); // 可以添加Integer到Number列表
              System.out.println("Number list: " + numberList);
              
              List<Object> objectList = new ArrayList<>();
              addNumbers(objectList); // 也可以添加到Object列表
              System.out.println("Object list: " + objectList);
          }
      }
      

      深度剖析

      有些小伙伴在工作中經(jīng)常混淆? extends? super,其實記住PECS原則就很簡單:

      PECS(Producer Extends, Consumer Super)

      • 當你是生產(chǎn)者(主要從集合讀取)時,使用? extends
      • 當你是消費者(主要向集合寫入)時,使用? super
      // 生產(chǎn)者 - 從src讀取數(shù)據(jù)
      public static <T> void copy(List<? extends T> src, List<? super T> dest) {
          for (T element : src) {
              dest.add(element); // src生產(chǎn),dest消費
          }
      }
      
      // 使用
      List<Integer> integers = List.of(1, 2, 3);
      List<Number> numbers = new ArrayList<>();
      copy(integers, numbers); // 正確:Integer extends Number, Number super Integer
      

      為了理解通配符的邊界概念,我畫了一個類型層次圖:

      image

      使用場景

      • 編寫通用工具方法
      • 處理未知類型的集合
      • 實現(xiàn)靈活的API接口
      • 框架設計中的類型抽象

      高級話題:泛型約束和最佳實踐

      了解了基本符號后,我們來看看一些高級用法和最佳實踐。

      泛型約束

      // 1. 多邊界約束
      public class MultiBound<T extends Number & Comparable<T> & Serializable> {
          private T value;
          
          public boolean isGreaterThan(T other) {
              return value.compareTo(other) > 0;
          }
      }
      
      // 2. 靜態(tài)方法中的泛型
      public class Utility {
          // 靜態(tài)方法需要聲明自己的泛型參數(shù)
          public static <T> T getFirst(List<T> list) {
              return list.isEmpty() ? null : list.get(0);
          }
          
          // 不能在靜態(tài)上下文中使用類的泛型參數(shù)
          // public static T staticMethod() { } // 編譯錯誤!
      }
      
      // 3. 泛型與反射
      public class ReflectionExample {
          public static <T> T createInstance(Class<T> clazz) {
              try {
                  return clazz.getDeclaredConstructor().newInstance();
              } catch (Exception e) {
                  throw new RuntimeException("創(chuàng)建實例失敗", e);
              }
          }
      }
      

      最佳實踐

      1. 命名約定

        • T:通用類型
        • E:集合元素
        • K:鍵
        • V:值
        • N:數(shù)字
        • S、U、V:第二、第三、第四類型參數(shù)
      2. 避免過度使用

         // 不好:過度泛型化
         public class OverGeneric<A, B, C, D> {
             public <E, F> E process(A a, B b, C c, D d, E e, F f) {
                 // 難以理解和維護
                 return e;
             }
         }
         
         // 好:適度使用
         public class UserService {
             public <T> T findUserById(String id, Class<T> type) {
                 // 清晰的意圖
             }
         }
      
      1. 處理類型擦除
         // 由于類型擦除,不能直接使用T.class
         public class TypeErasureExample<T> {
             // private Class<T> clazz = T.class; // 編譯錯誤!
             
             // 解決方案:傳遞Class對象
             private Class<T> clazz;
             
             public TypeErasureExample(Class<T> clazz) {
                 this.clazz = clazz;
             }
             
             public T createInstance() throws Exception {
                 return clazz.getDeclaredConstructor().newInstance();
             }
         }
      

      總結

      經(jīng)過以上介紹,相信你對Java泛型符號有了更深入的理解。

      符號對比

      符號 含義 使用場景 示例
      T 通用類型 工具類、不確定類型 Box<T>, Converter<T>
      E 元素類型 集合框架 List<E>, Set<E>
      K 鍵類型 鍵值對數(shù)據(jù)結構 Map<K, V>, Cache<K, V>
      V 值類型 鍵值對數(shù)據(jù)結構 Map<K, V>, Entry<K, V>
      ? 未知類型 靈活的方法參數(shù) List<?>, <? extends T>

      選擇原則

      1. 語義優(yōu)先

        • 集合元素用E
        • 鍵值對用K、V
        • 通用類型用T
        • 未知類型用?
      2. PECS原則

        • 生產(chǎn)者用? extends
        • 消費者用? super
      3. 可讀性優(yōu)先

        • 避免過度泛型化
        • 使用有意義的符號名
        • 適當添加文檔注釋

      我的一些建議

      有些小伙伴在工作中,可能一開始覺得泛型很復雜,但只要掌握了核心概念,就能寫出更安全、更靈活的代碼。記住這些要點:

      1. 類型安全是第一要務:讓錯誤在編譯期暴露
      2. 代碼即文檔:好的泛型使用能讓代碼自說明
      3. 平衡靈活性和復雜度:不要為了泛型而泛型
      4. 理解類型擦除:知道泛型在運行時的行為

      泛型是Java類型系統(tǒng)的重要組成,熟練掌握這些符號,能讓你在框架設計、工具開發(fā)、代碼重構中游刃有余。

      最后說一句(求關注,別白嫖我)

      如果這篇文章對您有所幫助,或者有所啟發(fā)的話,幫忙關注一下我的同名公眾號:蘇三說技術,您的支持是我堅持寫作最大的動力。

      求一鍵三連:點贊、轉(zhuǎn)發(fā)、在看。

      關注公眾號:【蘇三說技術】,在公眾號中回復:進大廠,可以免費獲取我最近整理的10萬字的面試寶典,好多小伙伴靠這個寶典拿到了多家大廠的offer。

      更多經(jīng)常內(nèi)容在我的技術網(wǎng)站:http://www.susan.net.cn

      posted @ 2025-10-24 09:21  蘇三說技術  閱讀(470)  評論(2)    收藏  舉報
      主站蜘蛛池模板: 四虎影视永久无码精品| 黄色国产精品一区二区三区| 日韩av天堂综合网久久| 国产精品有码在线观看| 国产资源精品中文字幕| 国产精品亚洲精品日韩已满十八小| 麻豆果冻传媒2021精品传媒一区| 国产乱老熟女乱老熟女视频| 日本亚洲欧洲无免费码在线| 国产va免费精品观看| 国产真实露脸乱子伦原著| 九九热免费精品视频在线| 亚洲色大成网站WWW久久| 精品999日本久久久影院| 韩国精品福利视频一区二区| 国精品无码一区二区三区在线看| 国产一区二区午夜福利久久| 国产精品久久久久影院| 日韩在线视频线观看一区| 日日碰狠狠添天天爽超碰97| 狠狠噜天天噜日日噜| 日韩国产中文字幕精品| 男女啪啪高潮激烈免费版| 国产午夜精品一区二区三| 无套内谢少妇高清毛片| 麻豆成人精品国产免费| 黄色三级亚洲男人的天堂| 极品vpswindows少妇| 特级做a爰片毛片免费看无码| 奇米777四色影视在线看| 国产精品中文字幕第一页| 亚洲欧美综合人成在线| 久久精品国产精品亚洲| 亚洲成人av高清在线| 亚洲肥老太bbw中国熟女| 日韩精品亚洲不卡一区二区| 日韩乱码人妻无码中文字幕视频 | 制服丝袜中文字幕在线| 91热在线精品国产一区| 成人午夜免费无码视频在线观看| 少妇厨房愉情理9仑片视频|