☰
Java的泛型(Generics)
2026/10/28 1:15:04 网站建设 项目流程

一、为什么需要泛型

泛型程序设计(generic programming)意味着编写的代码可以对多种不同类型的对象重用。

例如,你并不希望为收集String和File对象分别编写不同的类。实际上,也不需要这样做,因为一个 ArrayList类就可以收集任何类的对象。这就是泛型程序设计的一个例子。实际上,在Java有泛型类之前已经有一个ArrayList类。

在Java中增加泛型类之前,泛型程序设计是用继承实现的。Arraylist类只维护一个Object引用的数组:

这种方法存在两个问题:当获取一个值时必须进行强制类型转换。此外,这里没有错误检查。可以向数组列表中添加任何类的对象。

对于这个调用,编译和运行都不会出错。不过在其他地方,如果将取出的结果强制类型转换为其他子类型,可能就会产生一个错误。

对于集合来说,编译器只知道集合里放的是对象,也就是Object类型,所以,只要是Object类型,就可以放在这个集合中

ArrayList arrayList = new ArrayList(); arrayList.add(new Dog("花花",10)); arrayList.add(new Dog("旺财",6)); arrayList.add(new Dog("奇奇",7)); arrayList.add(new Cat("哈哈",9));

在遍历时,由于编译器只知道集合中的对象是Object类型,所以使用具体类型会报错

为了解决以上问题,泛型这个概念就出现了;

泛型类和泛型方法有类型参数,这使得它们可以准确地描述用特定类型实例化时会发生什么。在有泛型类之前,程序员必须使用Object编写适用于多种类型的代码。这很烦琐,也很不安全。
随着泛型的引人,Java有了一个表述能力很强的类型系统,允许设计者详细地描述变量和方法的类型要如何变化。对于简单的情况,你会发现实现泛型代码很容易。不过,在更高级的情况下,对于实现者来说这会相当复杂。其目标是提供让其他程序员可以轻松使用的类和方法而不会出现意外。
Java 5中泛型的引入成为Java程序设计语言自最初发行以来最显著的变化。Java的一个主要设计目标是支持与之前版本的兼容性。因此,Java的泛型有一些让人不快的局限性。

二、泛型介绍

三、泛型的语法

public class Main { public static void main(String[] args) { person person = new person<String,Integer>(); System.out.println(person.getClass()); } } class person<E,N>{ E name; N age; public person() { } public person(E name, N age) { this.name = name; this.age = age; } }

E和N的具体数据类型在创建这个类的对象时指定,即在编译时期,就确定E和N什么类型;

(只能是引用数据类型,不能是基本数据类型)

实际开发中我们一般不全写

我们往往会采用简写方式

编译器会根据前面的String帮我们推断出后面<>中也是String;

格式:public class 类名 <泛型类型1,...> { }

例如:public class GenericClass<ab,a,c> {}

理论上,参数类型可以是任意字母,当然,它也是有规范的,通常类型参数我们都使用大写的单个字母表示:

E - Element (在集合中使用,因为集合中存放的是元素)
T - Type(Java 类)
K - Key(键)
V - Value(值)
N - Number(数值类型)
? - 表示不确定的 java 类型


四、自定义泛型

①自定义泛型类




②自定义泛型接口

对于这样的一个泛型接口

public interface IUsb<U, R> { public abstract R get(U u); public abstract void hi(R r); public abstract void run(R r1, R r2, R r3, R r4); public default R method(U u) { return null; } }

需要在继承接口或者实现接口时指定其具体类型,编译器就会知道子接口用什么类型

Ⅰ.继承接口

interface IA extends IUsb<String,Integer>{ @Override Integer get(String s); @Override void hi(Integer integer); @Override void run(Integer r1, Integer r2, Integer r3, Integer r4); @Override default Integer method(String s) { return IUsb.super.method(s); } }

Ⅱ.实现接口

class AA implements IUsb<String,Double>{ @Override public Double get(String s) { return null; } @Override public void hi(Double aDouble) { } @Override public void run(Double r1, Double r2, Double r3, Double r4) { } @Override public Double method(String s) { return IUsb.super.method(s); } }

Ⅲ.注意事项:

因为具体类型在编译时就要确定,如果不指定具体类型,默认为Object

interface IA extends IUsb{ @Override Object get(Object o); @Override void hi(Object o); @Override void run(Object r1, Object r2, Object r3, Object r4); @Override default Object method(Object o) { return IUsb.super.method(o); } }
class AA implements IUsb{ @Override public Object get(Object o) { return null; } @Override public void hi(Object o) { } @Override public void run(Object r1, Object r2, Object r3, Object r4) { } @Override public Object method(Object o) { return IUsb.super.method(o); } }

③自定义泛型方法

非静态方法


静态方法



泛型方法与可变参数


Ⅰ.泛型方法可定义在普通类中

public class Main { public static void main(String[] args) { Car car = new Car(); // 使用泛型方法,直接在参数列表中传入参数,编译器会确定类型 car.fly("hjhdjs",100); } } class Car{ public void run(){ } // 1.<T,R>为泛型标识符,代表这是个泛型方法 public <T,R> void fly(T t,R r){ } }

Ⅱ.泛型方法可定义在泛型类中

class Fish<T, R> { public void run() { } public <U, M> void eat(U u, M m) { // 泛型方法 } }

Ⅲ.注意事项

泛型方法,既可以使用类声明的泛型,也可以使用自己声明的泛型

对于以下方法

public void run(T t) { }

只能说它使用了类声明的泛型,它不是一个泛型方法;

五、泛型的继承和通配符

5.1无界通配符


语法结构

示例

5.2通配符的上限限定

语法结构

示例

5.3通配符的下限限定

语法结构

示例

六、泛型总结

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询