嘿,各位 Java 码农小伙伴们!今天咱来唠唠 Java 里那个有点神秘又超有用的玩意儿 ------ 泛型。你是不是有时候看着代码里那些尖括号 <> ,心里犯嘀咕:这到底是干啥的?别慌,今儿个就给你整得明明白白,从为啥要用泛型,一路杀到泛型的中高级玩法,代码案例管够,包你学个通透!
为啥要有泛型这玩意儿?
咱先从一个简单的例子说起。假设你要写一个能装各种东西的容器,一开始可能会这么干:
java
import java.util.ArrayList;
import java.util.List;
public class WithoutGeneric {
public static void main(String[] args) {
List list = new ArrayList();
list.add("Hello, World!");
list.add(42);
list.add(3.14);
for (Object obj : list) {
String str = (String) obj; // 这里会报错,因为不是所有元素都是String
System.out.println(str);
}
}
}
这段代码乍一看没啥问题,咱往 List 里塞了字符串、整数还有浮点数。但是到了取数据的时候,就出幺蛾子了。你得手动把取出来的 Object 转成你想要的类型,而且要是转错了,运行时就给你抛个 ClassCastException ,这可太闹心了。
这时候泛型就闪亮登场啦!它就像是给你的容器贴上了标签,告诉编译器这个容器里装的到底是啥类型。
java
import java.util.ArrayList;
import java.util.List;
public class WithGeneric {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Hello, World!");
// list.add(42); 这行代码会报错,因为List被指定为只能装String类型
for (String str : list) {
System.out.println(str);
}
}
}
瞧见没,用了泛型之后,编译器就会帮你检查类型,往 List 里塞错东西直接报错,根本不让你运行。这就好比你有个标着 "只放袜子" 的抽屉,你要是硬塞个鞋子进去,它就会喊:"嘿,不对啊,这不是放鞋子的地儿!" 是不是超省心?
泛型的中高级玩法
泛型类
除了在集合里用,咱还能自己定义泛型类。比如说,咱做一个简单的二元组类:
csharp
public class Pair<T, U> {
private T first;
private U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public U getSecond() {
return second;
}
}
这里的 <T, U> 就是泛型参数, T 和 U 可以是任何类型。咱可以这样用它:
arduino
public class PairUsage {
public static void main(String[] args) {
Pair<String, Integer> pair = new Pair<>("Number of apples", 5);
String description = pair.getFirst();
Integer count = pair.getSecond();
System.out.println(description + ": " + count);
}
}
看,通过泛型,这个 Pair 类可以灵活地装不同类型的两个值,就像一个万能的小口袋,啥组合都能装,只要你告诉它装的是啥类型。
泛型方法
泛型可不只在类上能用,方法也能有泛型。假设你有一个工具类,里面有个方法能返回数组里最大的元素:
php
public class GenericMethod {
public static <T extends Comparable<T>> T max(T[] array) {
if (array == null || array.length == 0) {
return null;
}
T max = array[0];
for (int i = 1; i < array.length; i++) {
if (array[i].compareTo(max) > 0) {
max = array[i];
}
}
return max;
}
}
这里 <T extends Comparable> 表示 T 必须是实现了 Comparable 接口的类型,这样才能比较大小。咱可以这么调用这个方法:
ini
public class GenericMethodUsage {
public static void main(String[] args) {
Integer[] intArray = {1, 5, 3, 9, 7};
Integer maxInt = GenericMethod.max(intArray);
System.out.println("Max int: " + maxInt);
String[] stringArray = {"banana", "apple", "cherry"};
String maxString = GenericMethod.max(stringArray);
System.out.println("Max string: " + maxString);
}
}
这个 max 方法就像一个超级裁判,不管你给它啥类型的数组(只要能比较大小),它都能给你找出最大的那个,是不是很厉害?
通配符
通配符也是泛型里一个很有用的东西。比如说,有个方法要打印一个 List 里的所有元素,但这个 List 可以是任何类型,这时候就可以用通配符 ? :
java
import java.util.List;
public class WildcardExample {
public static void printList(List<?> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
}
调用的时候:
java
import java.util.ArrayList;
import java.util.List;
public class WildcardUsage {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
intList.add(1);
intList.add(2);
WildcardExample.printList(intList);
List<String> stringList = new ArrayList<>();
stringList.add("Hello");
stringList.add("World");
WildcardExample.printList(stringList);
}
}
这里的 List<?> 就像是一个万能的 List 接收器,不管你传进来的是 List 还是 List ,它都能照单全收,然后打印出来,是不是很方便?
还有更高级的,比如 <? extends Type> 和 <? super Type> ,这俩就像是给通配符加了限制。 <? extends Type> 表示这个类型是 Type 的子类或者就是 Type 本身, <? super Type> 则表示这个类型是 Type 的父类或者就是 Type 本身。
举个例子,假设你有个动物类 Animal ,还有它的子类 Cat 和 Dog :
scala
class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
有个方法要处理所有 Cat 及其子类的 List ,就可以这么写:
java
import java.util.List;
public class UpperBoundWildcard {
public static void processCats(List<? extends Cat> catList) {
for (Cat cat : catList) {
// 这里可以对cat进行操作
}
}
}
调用的时候:
scala
import java.util.ArrayList;
import java.util.List;
public class UpperBoundUsage {
public static void main(String[] args) {
List<Cat> catList = new ArrayList<>();
catList.add(new Cat());
UpperBoundWildcard.processCats(catList);
// 假设还有个SiameseCat类继承自Cat
class SiameseCat extends Cat {}
List<SiameseCat> siameseCatList = new ArrayList<>();
siameseCatList.add(new SiameseCat());
UpperBoundWildcard.processCats(siameseCatList);
}
}
而 <? super Type> 则相反,它允许传入 Type 的父类类型的 List 。比如有个方法要往 List 里添加 Cat ,就可以用 <? super Cat> :
java
import java.util.List;
public class LowerBoundWildcard {
public static void addCat(List<? super Cat> catList) {
catList.add(new Cat());
}
}
调用的时候:
java
import java.util.ArrayList;
import java.util.List;
public class LowerBoundUsage {
public static void main(String[] args) {
List<Cat> catList = new ArrayList<>();
LowerBoundWildcard.addCat(catList);
List<Animal> animalList = new ArrayList<>();
LowerBoundWildcard.addCat(animalList);
}
}
这样,通过通配符的上下界,你可以更精确地控制泛型的类型范围,就像给你的代码加了个灵活又精准的过滤器。
好啦,小伙伴们,今天关于 Java 泛型的从入门到中高级玩法就讲到这儿啦!希望你看完这篇文章,对泛型不再迷糊,能在代码里把它用得飞起!要是还有啥问题,评论区留言,咱一起唠唠。