泛型通配符 T、E、K、V、?

在Java后端開發中,你一定在寫集合類或工具類時,見過 T、E、K、V、? 這樣的泛型通配符。但你是否有過以下疑惑:

  • T、E、K、V 到底有什么區別?為什么大家都用這些字母?
  • List<?> 和 List 有什么不同?什么時候該用通配符,什么時候該用類型參數?
  • 如果不用泛型,代碼也能跑,為什么一定要用泛型?

1. 為什么要用泛型

類型不安全與強制轉換

假設我們要寫一個簡單的盒子類,用來存放物品:

// 沒有泛型的盒子類
public class Box {private Object item;  // 只能用Object存儲任何類型public void setItem(Object item) {this.item = item;}public Object getItem() {return item;}
}

使用方式:?

public static void main(String[] args) {Box box = new Box();box.setItem("Hello");  // 存入StringString s = (String) box.getItem();  // 必須強制轉換回Stringbox.setItem(123);      // 也可以存入IntegerString i = (String) box.getItem();  // 但這里會拋出ClassCastException!
}

問題:??

  • ??類型不安全??:可以存入任何類型(String、Integer等),但取出時容易忘記轉換或轉換錯誤
  • ??繁瑣的強制轉換??:每次取出都要手動cast
  • ??運行時錯誤??:如果類型轉換錯了,只能在運行時才發現(拋出ClassCastException)

使用泛型后**

// 泛型盒子類
public class Box<T> {private T item;  // T是類型參數public void setItem(T item) {this.item = item;}public T getItem() {return item;  // 不需要強制轉換}
}public static void main(String[] args) {Box<String> stringBox = new Box<>();stringBox.setItem("Hello");String s = stringBox.getItem();  // 自動就是String類型,無需轉換Box<Integer> intBox = new Box<>();intBox.setItem(123);Integer i = intBox.getItem();  // 自動就是Integer類型stringBox.setItem(123);  // 編譯錯誤!不能放入Integer}

2. T、E、K、V、? 的含義

首先,我們要明確一個概念,TEKV類型參數(Type Parameter),而?通配符(Wildcard)。他們雖然都用在泛型中,但扮演的角色完全不同。Java 官方并沒有強制規定這些字母的含義,只是社區形成了約定俗成的寫法。常見規則如下:

符號常見含義使用場景
TType(類型)通用類型,最常見
EElement(元素)集合中的元素
KKey(鍵)映射的鍵(Map)
VValue(值)映射的值(Map)
?通配符表示未知類型,常用于 API 的參數或返回值

2.1 使用 T (Type,任意類型)

示例:API響應包裝器

// 使用 T 定義一個通用的API響應類
public class ApiResponse<T> {private int code;private String message;private T data; // T 代表響應的業務數據類型// 構造方法public ApiResponse(int code, String message, T data) {this.code = code;this.message = message;this.data = data;}// 成功響應的靜態工廠方法public static <T> ApiResponse<T> success(T data) {return new ApiResponse<>(200, "成功", data);}public static ApiResponse<?> error(int code, String message) {return new ApiResponse<>(code, message, null);}// Getter 和 Setterpublic T getData() {return data;}public void setData(T data) {this.data = data;}// ... 其他getter/setter
}// 業務實體
public class User {private Long id;private String name;private String email;// ... 構造方法、getter、setter
}public class Product {private Long id;private String name;private BigDecimal price;// ... 構造方法、getter、setter
}// 在Service層使用
public class UserService {public ApiResponse<User> getUserById(Long id) {User user = userRepository.findById(id);if (user != null) {return ApiResponse.success(user); // T 被推斷為 User} else {return ApiResponse.error(404, "用戶不存在");}}
}public class ProductService {public ApiResponse<List<Product>> getFeaturedProducts() {List<Product> products = productRepository.findFeatured();return ApiResponse.success(products); // T 被推斷為 List<Product>}
}// Controller層調用
@GetMapping("/users/{id}")
public ApiResponse<User> getUser(@PathVariable Long id) {return userService.getUserById(id);// 返回: {"code":200,"message":"成功","data":{"id":1,"name":"張三","email":"zhang@example.com"}}
}@GetMapping("/products/featured")
public ApiResponse<List<Product>> getFeaturedProducts() {return productService.getFeaturedProducts();// 返回: {"code":200,"message":"成功","data":[{"id":101,"name":"手機","price":2999.00}]}
}

2.2 E(Element,集合中的元素)

示例:樹形結構節點

// 通用樹節點(可用于組織架構、分類目錄等)
public class TreeNode<E> {private E data;private List<TreeNode<E>> children;public void addChild(TreeNode<E> child) {if (children == null) children = new ArrayList<>();children.add(child);}
}// 使用示例
TreeNode<String> root = new TreeNode<>();
root.setData("總公司");TreeNode<String> branch1 = new TreeNode<>();
branch1.setData("北京分公司");
root.addChild(branch1);TreeNode<String> branch2 = new TreeNode<>();
branch2.setData("上海分公司");
root.addChild(branch2);

2.3 類型參數 K(Key)和 V(Value)——鍵值對

示例:本地緩存類

// 本地緩存實現
public class LocalCache<K, V> {private Map<K, V> cache = new ConcurrentHashMap<>();private long expireTime;public void put(K key, V value) {cache.put(key, value);}public V get(K key) {return cache.get(key);}
}// 使用示例
LocalCache<Long, User> userCache = new LocalCache<>();
userCache.put(1001L, new User(1001L, "Alice"));LocalCache<String, List<Product>> categoryCache = new LocalCache<>();
categoryCache.put("electronics", Arrays.asList(new Product(...), ...));

2.4 通配符 ? ——處理未知類型

Java 泛型通配符主要有三種形態

1)無界通配符 ?

無界通配符表示可以匹配任何類型,適用于不確定或無關具體類型的情況。
示例:打印任意集合元素

import java.util.*;public class Demo1 {public static void printList(List<?> list) {for (Object element : list) {System.out.println(element);}}public static void main(String[] args) {List<String> names = Arrays.asList("Tom", "Jerry");List<Integer> scores = Arrays.asList(88, 99);printList(names);  // 輸出 Tom, JerryprintList(scores); // 輸出 88, 9}
}

特點:

  • 可以接收任何類型的 List。
  • 只能讀取元素,不能隨意 add。
2)上界通配符 ? extends T

表示“某種類型是 T 或 T 的子類”,適合生產者 / 只讀場景(PECS 原則中的 Producer)。
示例:打印數字列表

import java.util.*;public class Demo1 {public static void printNumbers(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}}public static void main(String[] args) {List<Integer> ints = Arrays.asList(1, 2, 3);List<Double> doubles = Arrays.asList(1.1, 2.2, 3.3);printNumbers(ints);    // Integer extends NumberprintNumbers(doubles); // Double extends Number}
}

特點:

  • 可以讀取元素為 Number 類型。
  • 不能寫入 list.add(…),因為不知道具體是 Integer 還是 Double。
3)下界通配符 ? super T

表示“某種類型是 T 或 T 的父類”,適合消費者 / 寫入場景(PECS 原則中的 Consumer)。
示例:向集合中添加數字

import java.util.*;public class Demo3 {public static void addNumbers(List<? super Integer> list) {list.add(10);list.add(20);}public static void main(String[] args) {List<Number> numbers = new ArrayList<>();List<Object> objects = new ArrayList<>();addNumbers(numbers); // Number 是 Integer 的父類addNumbers(objects); // Object 是 Integer 的父類System.out.println(numbers); // 輸出 [10, 20]System.out.println(objects); // 輸出 [10, 20]}
}

特點:

  • 可以安全向集合寫入 Integer 類型。
  • 讀取出來的元素只能當作 Object,因為類型不確定
總結
通配符含義適用場景示例特點
?無界泛用工具、只讀 API可以讀取,不能寫入,適合日志/導出/打印等
? extends T上界生產者 / 只讀可以讀取 T 或其子類,不能寫入
? super T下界消費者 / 寫入可以寫入 T 類型,讀取只能當 Object

3. 通配符中的PECS原則

PECS 是 Java大師Joshua Bloch 在《Effective Java》里提出的一個泛型使用經驗法則,用來指導我們在選擇通配符時,應該用 extends 還是 super。

  • Producer Extends:如果參數是生產者(提供數據給你),就用 ? extends T。
  • Consumer Super:如果參數是消費者(你要把數據放進去),就用 ? super T。

簡單一句話:

  • 讀(生產者)用 extends,寫(消費者)用 super。

示例 1:Producer(讀數據)
假設我們有個方法,需要從集合里讀取元素:

public static void printNumbers(List<? extends Number> list) {for (Number n : list) {System.out.println(n);}
}
  • list 是一個 生產者(提供數字給我們打印),所以用 ? extends Number,允許 List、List 傳進來。

示例 2:Consumer(寫數據)
假設我們有個方法,需要往集合里寫入數據:

public static void addIntegers(List<? super Integer> list) {list.add(1);list.add(2);
}

list 是一個 消費者(我們往里面放 Integer)所以用 ? super Integer,允許 List、List、List 傳進來。

4. 注意事項

  • 能用泛型參數就別用 Object,除非你明確就是要“任意類型”,否則優先用泛型。
  • 合理選擇通配符 ? , 只讀數據 → ? extends T,只寫數據 → ? super T
  • 不要濫用泛型,有些場景寫成泛型反而增加理解成本,比如方法內部只操作 String,就直接用 String

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/100434.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/100434.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/100434.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于腳手架微服務的視頻點播系統-數據管理與網絡通信部分的預備工作

基于腳手架微服務的視頻點播系統-數據管理與網絡通信部分的預備工作一.數據管理二.網絡通信2.1客戶端通信模塊及測試用例的實現2.2MockServer搭建的相關接口介紹2.3MockServer的搭建示例一.數據管理 在前?的實現中&#xff0c;程序中的數據、以及界?操作等全部攪合在?起&am…

html表單登錄模式代碼

使用的是Content-Typeapplication/x-www-form-urlencoded形式如代碼如下的html&#xff0c;后端沒寫下去&#xff1a;<!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><title>管理員登錄</title><…

NLP:Transformer之self-attention(特別分享3)

本文目錄&#xff1a;一、核心思想&#xff1a;一句話概括二、計算流程的直觀比喻三、分步計算詳解&#xff08;附數字例子&#xff09;第 1 步&#xff1a;創建 Query, Key, Value 向量第 2 步&#xff1a;計算注意力分數第 3 步&#xff1a;縮放并應用 Softmax第 4 步&#x…

25、優化算法與正則化技術:深度學習的調優藝術

學習目標:深入理解各種優化算法的原理和適用場景,掌握學習率調度的方法和策略,學會使用Dropout、批歸一化等正則化技術,理解早停和驗證策略,建立深度學習調優的系統性知識。 深度學習的成功不僅依賴于精巧的模型架構,更在于巧妙的訓練策略。如果說網絡架構是房屋的設計圖…

Netty-01-NIO前置知識

目錄 NIO三大組件 一. ByteBuffer 基本用法 DirectByteBuffer與HeapByteBuffer對比 字符串轉ByteBuffer ByteBuffer.wrap(byte[] ) 粘包與拆包 文件編程 零拷貝transferTo 二. 阻塞與非阻塞Channel 三. Selector SelectionKey&#xff08;重點&#xff09; Select…

知識點17:多Agent系統架構設計模式

知識點17&#xff1a;多Agent系統架構設計模式 核心概念 掌握系統架構思維&#xff0c;理解多Agent系統的設計原則和模式 架構設計核心概念 在構建多Agent系統時&#xff0c;良好的架構設計是系統成功的關鍵。本節將介紹多Agent系統架構設計中的核心概念&#xff0c;包括單點瓶…

數據庫造神計劃第五天---增刪改查(CRUD)(1)

&#x1f525;個人主頁&#xff1a;尋星探路 &#x1f3ac;作者簡介&#xff1a;Java研發方向學習者 &#x1f4d6;個人專欄&#xff1a;《從青銅到王者&#xff0c;就差這講數據結構&#xff01;&#xff01;&#xff01;》、 《JAVA&#xff08;SE&#xff09;----如此簡單&a…

基于Vue3的人工智能生成內容標識服務平臺前端頁面設計

效果圖&#xff1a;素材庫&#xff1a;App.vue<template><div id"app"><!-- 頭部導航 --><Header /><!-- 主要內容區域 --><main class"main-content"><div class"container"><!-- 強制性國家標準…

使用 MyCat 實現 MySQL 主從讀寫分離

文章目錄使用 MyCat 實現 MySQL 主從讀寫分離完整指南一、MySQL 讀寫分離基礎概述1.1 讀寫分離工作原理1.2 為什么需要讀寫分離1.3 讀寫分離的兩種實現方式主流讀寫分離中間件對比二、MyCat 中間件簡介2.1 MyCat 核心功能2.2 MyCat 適用場景三、環境準備與 MyCat 安裝3.1 前提&…

物聯網傳感器檢測實驗

/*------------------------------------------------------------------------------ * @文件名 : handle * @描述 : 用戶處理函數 * @作者 : 物聯網項目組 * @日期 : 2023/04/01 * @版本 : V0.0.2 *****************************…

什么是dirsearch、xray、durpsuite、sqlmap?

你提到的 dirsearch、xray、durpsuite&#xff08;可能為筆誤&#xff0c;推測是 ??Burp Suite??&#xff09;和 sqlmap 均為網絡安全領域中常用的工具&#xff0c;主要用于 Web 應用的安全測試、漏洞檢測或滲透測試。以下分別詳細說明&#xff1a;??1. dirsearch????…

lamp腳本部署

#!/bin/bash #關閉防火墻和selinux systemctl stop firewalld systemctl disable firewalld setenforce 0 #配置yum網絡源 echo “正在配置yum倉庫” rm -rf /etc/yum.repos.d/* wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo &am…

Redis Hash數據類型深度解析:從命令、原理到實戰場景

前言 在Redis的眾多數據結構中&#xff0c;Hash&#xff08;哈希&#xff09;類型占據著至關重要的地位。Redis本身就是一個高性能的鍵值&#xff08;Key-Value&#xff09;數據庫&#xff0c;其底層的鍵值對便是通過哈希方式組織的。而Hash數據類型則更進一步&#xff0c;它允…

【C++實戰?】解鎖C++文件操作:從基礎到實戰的進階之路

目錄一、文件操作的基本概念1.1 文件的分類與打開方式1.2 文件流的概念與相關類&#xff08;ifstream、ofstream、fstream&#xff09;1.3 文件操作的基本流程二、文本文件的讀寫實戰2.1 文本文件的打開與關閉2.2 文本文件的寫入操作&#xff08;<< 運算符、write 函數&a…

從C++開始的編程生活(9)——模板初階

前言 本系列文章承接C語言的學習&#xff0c;需要有C語言的基礎才能學會哦~ 第8篇主要講的是有關于C的模板初階。 C才起步&#xff0c;都很簡單&#xff01;&#xff01; 目錄 前言 模板初階 基本語法 函數模板的實例化 顯式實例化的作用 類模板 基本語法 模板初階 模板…

計算機網絡——傳輸層(25王道最新版)

傳輸層傳輸層提供的服務進程 端口號 傳輸層協議之間的關系socket套接字有鏈接 VS 無連接 | 可靠 VS 不可靠UDP數據報及檢驗數據報格式檢驗方法TCPTCP協議的三大階段TCP報文段格式&#xff08;很重要&#xff09;建立連接&#xff08;三次握手&#xff09;&#xff08;超級超級重…

羽毛球地板:從專業運動場景到全民健身市場的技術躍遷與產業重構

在全球體育產業向“專業化大眾化”雙軌并行的趨勢下&#xff0c;羽毛球地板作為運動場景的核心基礎設施&#xff0c;正經歷從單一功能型產品向“性能優化場景適配智能管理”一體化解決方案的轉型。據QYResearch統計&#xff0c;2031年全球羽毛球地板市場規模將達15.95億元&…

R 語言查看類庫源碼的方法

你想查看 getGEO&#xff08;來自 R 語言 GEOquery 包&#xff09;的源碼&#xff0c;這能幫你更好理解其工作原理和數據處理細節。由于 getGEO 是 R 函數&#xff0c;查看方法與 Python 有所不同。下面為你提供幾種主要方法。 方法 適用場景 關鍵命令/操作 在 R 控制臺直接查看…

SQL,posexplode 用法示例

示例1 -- 創建測試數據 WITH test_data AS (SELECT array(apple, banana, cherry) as fruits ) SELECT pos, col FROM test_data LATERAL VIEW posexplode(fruits) t AS pos, col;結果 pos | col ----|------- 0 | apple 1 | banana 2 | cherry示例2 -- 假設有一個用戶表…

數據庫造神計劃第十天---數據庫約束(1)

&#x1f525;個人主頁&#xff1a;尋星探路 &#x1f3ac;作者簡介&#xff1a;Java研發方向學習者 &#x1f4d6;個人專欄&#xff1a;《從青銅到王者&#xff0c;就差這講數據結構&#xff01;&#xff01;&#xff01;》、 《JAVA&#xff08;SE&#xff09;----如此簡單&a…