泛型,通配符,泛型的使用与理解

泛型(eclipse)

泛型的定义

通常来讲,我们创建一个List集合,什么东西都是可以在里面放的,char也好,int也好,乱七八糟,我们这是常常会看到eclipse的左面显示着黄色的警告,虽然程序员不关心警告,但是去掉警告多少会舒心不是?
泛型,在jdk1.5加入,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。

List<Integer> list = new ArrayList<Integer>();
list.add(1);

这里指定泛型为Integer,这样如果我们使用list的添加方法,就只能添加int的数据类型,否则会报编译错误
当然了,<>里面并不是只能添加数据类型,对象也来者不拒
泛型方法的格式:

  • [访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
    其实这里的抛出异常可以不写,如果不需要的话

泛型与继承

// 泛型和继承的关系
class A<T> {
	
	protected T t;
	public T getT() {
		return t;
	}
}

class B1 extends A {} // 子类无视泛型 , 导致泛型类型永远是Object, 类型模糊

class B2 extends A<String> {} // 子类直接把泛型父类的泛型类写死, 子类中继承的T类型永远是固定的
class B3 extends A<Boolean> {}

class C<T> extends A<T> {} // 子类也泛型, 在子类对象创建时再动态决定泛型类型, 这是最灵活的做法.

注意
String是object的子类,但是泛型为String类型的List不是泛型为Object的List的子类

使用类型通配符:?

?是一个模糊类型,因此我们可以认为?比object包含的范围更大
比如List<?>是List< Object>或List< String>的父类
当然有一个例外

List<?> list = new ArrayList<String>();
		list.add(null);
		//list.add("ad");//编译错误
		list.get(0);

在这里添加null不报错是因为null是一个例外,这个null是所有类型的成员
而我们不知道修饰list的泛型是什么,所以会报错
我们可以调用get方法返回这个未知的数,假设我们忘了刚才添加的东西
那么get方法肯定有效,因为我们知道他总会返回一个Object类型

public static void main(String[] args) {
		List<?> list = null;
		list = new ArrayList<String>();
		list = new ArrayList<Double>();
		// list.add(3);
		list.add(null);

		List<String> l1 = new ArrayList<String>();
		List<Integer> l2 = new ArrayList<Integer>();
		l1.add("尚硅谷");
		
		l2.add(15);
		read(l1);
		read(l2);
	}

	static void read(List<?> list) {
		for (Object o : list) {
			System.out.println(o);
		}
	}

泛型类使用

我们使对象实例化之后不定义泛型的时候,默认为object
加入集合里的类型必须与指定的泛型一致
指定泛型为Integer就只能加入int类型的数据,否则会编译出错
泛型类可以派生子类病具体化
如泛型为object的类的子类可以泛型是integer

class T<Oject>{	}
class A extends T<Integer>{	}

反过来就不可以了,就好比你爸是百万富翁,你可以继承百万财产,也可以继承十万财产,这个随你喜欢
但是你爸现在手里只有十块钱,那你总不可以对你爸说你要继承一百万财产,这个肯定不行的,除非你去抢银行
不能在catch中使用泛型

try {	
		} catch (Exception e) {
			List<Integer> list2 = new ArrayList<Integer>();
			list2.add(1);
			System.out.println(list2);			
		} 

这个例子看到了么,catch通过了,但是很遗憾这个并不是泛型
我刚开始也是不明白泛型的区别,于是我们来斟酌这个词的意思好了,泛型是一种广泛的类型,也就是这个泛型写在那里,我们也不知道会什么类型,只有实例化的时候才会考虑到用什么类型来接收数据,在这之前,就让这个类型模糊化好了

Java 中的 **通配符(Wildcard)** 是用于处理泛型类不确定时的灵活机制,主要通过 `?` 来表示。它在集合操作、方法参数设计和继承关系中非常有用。 --- ## 🧩 一、三种通配符及其使用场景 | 通配符 | 名称 | 含义 | |--------|------|------| | `?` | 无界通配符 | 表示“任意类” | | `? extends T` | 上界通配符 | 表示“T 或其子类” | | `? super T` | 下界通配符 | 表示“T 或其父类” | 我们来逐个讲解它们的使用场景,并给出代码示例。 --- ### ✅ 1. 无界通配符:`List<?>` #### 使用场景: 当你只想读取集合内容,而不进行写入操作时,可以使用 `List<?>` 来接受任何泛型类的列表。 ```java public static void printList(List<?> list) { for (Object elem : list) { System.out.println(elem); } // ❌ 不能添加元素(除了 null) // list.add("hello"); // 编译错误! } // 使用 List<String> strings = Arrays.asList("A", "B"); List<Integer> integers = Arrays.asList(1, 2); printList(strings); // ✔️ printList(integers); // ✔️ ``` > 🔍 原因:因为 `?` 可以是任何类,编译器无法确定具体类,所以禁止写入(防止类不安全),但可以读为 `Object`。 --- ### ✅ 2. 上界通配符:`? extends T` #### 使用场景: 适用于“生产者”场景(Producer),即你从集合中读取数据,且希望支持 `T` 及其所有子类。 ##### 示例:计算数字列表的总和 ```java public static double sum(List<? extends Number> numbers) { double total = 0.0; for (Number num : numbers) { total += num.doubleValue(); } return total; } // 使用 List<Integer> ints = Arrays.asList(1, 2, 3); List<Double> doubles = Arrays.asList(1.5, 2.5); System.out.println(sum(ints)); // ✔️ Integer 是 Number 的子类 System.out.println(sum(doubles)); // ✔️ Double 是 Number 的子类 ``` > ✅ 允许传入 `List<Integer>`、`List<Double>` 等 > ❌ 不能向 `numbers` 中添加任何非 `null` 的元素(因为不知道确切类) --- ### ✅ 3. 下界通配符:`? super T` #### 使用场景: 适用于“消费者”场景(Consumer),即你要往集合中写入 `T` 类的数据。 ##### 示例:将整数加入一个只接受 `Integer` 及其父类的列表 ```java public static void addNumbers(List<? super Integer> list) { list.add(100); list.add(200); // 此时可以安全地添加 Integer } // 使用 List<Integer> ints = new ArrayList<>(); List<Number> numbers = new ArrayList<>(); List<Object> objects = new ArrayList<>(); addNumbers(ints); // ✔️ Integer → Integer addNumbers(numbers); // ✔️ Number 是 Integer 的父类 addNumbers(objects); // ✔️ Object 是 Integer 的父类 ``` > ✅ 可以添加 `Integer` > 🔍 读取时只能作为 `Object` 处理(不推荐从中读取) --- ## 📚 PECS 原则(记住关键口诀) 来自 Effective Java 的经典原则: > **PECS: Producer-Extends, Consumer-Super** | 场景 | 使用 | |------|------| | 如果你要从集合中**读取**数据(它是生产者)→ 使用 `? extends T` | | 如果你要向集合中**写入**数据(它是消费者)→ 使用 `? super T` | ### 经典案例:`Collections.copy()` 方法 ```java public static <T> void copy(List<? super T> dest, List<? extends T> src) ``` - `src` 是生产者 → `? extends T` - `dest` 是消费者 → `? super T` ```java List<Integer> src = Arrays.asList(1, 2, 3); List<Number> dest = new ArrayList<>(Arrays.asList(0.0, 0.0, 0.0)); Collections.copy(dest, src); // 成功复制 ``` --- ## ⚠️ 注意事项常见错误 | 错误 | 解释 | |------|------| | `List<?> list = new ArrayList<String>(); list.add("hi");` | ❌ 编译失败,不允许添加 | | `List<? extends Number> list = new ArrayList<Integer>(); list.add(new Integer(1));` | ❌ 即使是 Integer 也不行!因为可能是 `Double` 的引用 | | 把 `List<Object>` 当作 `List<String>` 的父类 | ❌ 错误理解没有这种继承关系 | --- ## ✅ 实际开发中的典应用场景 ### 场景 1:API 接口返回结果 ```java public Result<? extends User> getUser() { if (Math.random() > 0.5) { return Result.success(new AdminUser()); } else { return Result.success(new NormalUser()); } } ``` ### 场景 2:通用工具类处理多种子类 ```java public static void processAnimals(List<? extends Animal> animals) { for (Animal a : animals) { a.makeSound(); } } ``` ### 场景 3:构建灵活的 Builder 模式 ```java public class CollectionBuilder<T> { private final List<T> items = new ArrayList<>(); public CollectionBuilder<T> addAll(Collection<? extends T> coll) { items.addAll(coll); return this; } public List<T> build() { return new ArrayList<>(items); } } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值