「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!」
虽然写了一段时间的 Java 代码,但是一直以来只要看到泛型,还是会摸不着头脑选择性放弃。但是今天在看公司大佬的代码发现很多地方会用到泛型,所以今天就整理一下我理解的泛型。
什么是泛型
泛型程序设计(generic programming)是程序设计语言的一种风格或范式。泛型允许程序员在强类型程序设计语言中编写代码时使用一些以后才指定的类型,在实例化时作为参数指明这些类型。 泛型的定义主要有以下两种: 1⃣️ 在程序编码中一些包含类型参数的类型,也就是说泛型的参数只可以代表类,不能代表个别对象。 2⃣️ 在程序编码中一些包含参数的类。其参数可以代表类或对象等等。
👆上面的解释来自百度百科,简单的说 泛型是一种代码范式。
Java 泛型
Java 泛型的参数只可以代表类,不能代表个别对象。由于 Java 泛型的类型参数之实际类型在编译时会被消除,所以无法在运行时得知其类型参数的类型,而且无法直接使用基本值类型作为泛型类型参数。
Java 泛型设计原则:
把类型明确的工作推迟到创建对象或调用方法的时候才去明确的特殊的类型。只要在编译时期没有出现警告,那么运行时期就不会出现 ClassCastException
异常。
下面是简单的泛型的例子:
public class Generic {
public static void main(String[] args) {
// C 可以接受 String
C<String> stringC = new C<>("Hello world");
System.out.println(stringC);
// C 可以接受 Integer
C<Integer> integerC = new C<>(100);
System.out.println(integerC);
}
}
// C 可以接受任意类型数据的初始化
class C<T> {
private T item;
public C(T t){
item = t;
}
@Override
public String toString() {
return item + "";
}
}
T、E、K、V
T、E、K、V 本质上都是通配符。编码有时候讲究约定俗成。比如我们可以用 A-Z 之间的任何一个字母都可以表示通配符,并不会影响程序的正常运行。通常情况下 T、E、K、V 是这样约定的:
?
表示不确定的 Java 类型T ( type )
表示具体的一个 Java 类型K V ( key value )
分别代表 Java 键值中的 Key ValueE ( element )
代表 Element
通配符
-
上界通配符
? extends T
例如
List< ? extends Number>
目的是为了接受List<Integer> or List<Long>
的数据类型。 -
下界通配符
? super T
例如
List<? super Number>
目的是为了弥补只能存放一种特定类型数据的不足。
public class Generic {
public static void main(String[] args) {
// 可以
List<? super Number> list = new ArrayList<>();
list.add(10);
list.add(20L);
list.add(20.2);
System.out.println(list);
List<Integer> list1 = new ArrayList<>();
List<Long> list2 = new ArrayList<>();
list1.add(10);
list1.add(20);
list2.add(10L);
list2.add(20L);
read(list1);
read(list2);
}
private static void read(List< ? extends Number> list) {
Number first = list.get(0);
Number last = list.get(list.size() - 1);
System.out.println("first = " + first.toString() + " last = " + last.toString());
}
}
最后看一下 PECS(Producer Extends Consumer Super)原则:
- 频繁往外读取内容的,适合用上界 Extends
- 经常往里插入的,适合用下界 Super
泛型案例
// 多参数泛型
class Person<N, A, S> {
private N name;
private A age;
private S sex;
Person(N name, A age, S sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public N getName() {
return name;
}
public void setName(N name) {
this.name = name;
}
public A getAge() {
return age;
}
public void setAge(A age) {
this.age = age;
}
public S getSex() {
return sex;
}
public void setSex(S sex) {
this.sex = sex;
}
@Override
public String toString() {
return "姓名:" + name + ",年龄:" + age + ",性别:" + sex;
}
}
// 使用
public class Generic {
public static void main(String[] args) {
Person<String, Integer, Character> person = new Person<String, Integer, Character>("蛋炒饭", 28, '男');
printPerson(person);
// 姓名:蛋炒饭,年龄:28,性别:男
}
// 泛型方法
private static <T> void printPerson(T person){
if ( person != null) {
System.out.println(person);
}
}
}