深入理解 Java 泛型:从为啥用到咋高级用

嘿,各位 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 泛型的从入门到中高级玩法就讲到这儿啦!希望你看完这篇文章,对泛型不再迷糊,能在代码里把它用得飞起!要是还有啥问题,评论区留言,咱一起唠唠。

相关推荐
C1829818257510 分钟前
HttpURLConnection 与其他客户端关系
java
通往曙光的路上26 分钟前
发邮件1、创建邮箱
java
serendipity_hky27 分钟前
【SpringCloud | 第5篇】Seata分布式事务
分布式·后端·spring·spring cloud·seata·openfeign
五阿哥永琪42 分钟前
Spring Boot 中自定义线程池的正确使用姿势:定义、注入与最佳实践
spring boot·后端·python
麦麦鸡腿堡1 小时前
Java_类的加载
java·开发语言
Victor3561 小时前
Netty(16)Netty的零拷贝机制是什么?它如何提高性能?
后端
JIngJaneIL1 小时前
基于java + vue校园快递物流管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js
Victor3561 小时前
Netty(15)Netty的线程模型是什么?它有哪些线程池类型?
后端
超级大只老咪1 小时前
数组的正向存储VS反向存储(Java)
java·开发语言·python