一文学懂Java泛型
- 1.什么是Java泛型
- 2.泛型的快速入门
- 3.拥有泛型特性的类
- 4.泛型的使用细节
- 5.自定义泛型类
- 6.自定义泛型接口
- 7.自定义泛型方法
- 8.泛型通配符
1.什么是Java泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
假定我们有这样一个需求:写一个排序方法,能够对整型数组、字符串数组甚至其他任何类型的数组进行排序,该如何实现?
答案是可以使用 Java 泛型。
使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序。然后,调用该泛型方法来对整型数组、浮点数数组、字符串数组等进行排序。
2.泛型的快速入门
代码示例:
import java.util.ArrayList;
/**
* java泛型
*/
public class Generics {
public static void main(String[] args) {
// 使用java泛型对数据类型做限制
// 限制添加的类型必须为Dog
ArrayList<Dog> arrayList = new ArrayList<Dog>();
arrayList.add(new Dog("康康",12));
arrayList.add(new Dog("旺旺",6));
System.out.println(arrayList);
}
}
class Dog {
private String name;
private int age;
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
使用泛型,可以直接对于数据进行遍历,无需进行额外的类型强转,这增大了代码的效率:
for (Dog dog : arrayList) {
System.out.println(dog.getName());
System.out.println(dog.getAge());
}
3.拥有泛型特性的类
我们来创建一个类:
该类的一个属性为泛型,可以是任意的数据类型,在编译期间,确定E是什么类型
// 泛型类
class Person<E> {
E s; // 在编译期间,确定E是什么类型
public Person(E s) { // E可以是参数类型
this.s = s;
}
public E f() { // E也可以是返回类型
return s;
}
@Override
public String toString() {
return "Person{" +
"s=" + s +
'}';
}
}
那么我们在创建对象的时候,就可以传入不同的数据类型的数据,这很方便:
例如:
Person<String> stringPerson = new Person<String>("www");
System.out.println(stringPerson); // Person{s=www}
Person<Integer> integerPerson = new Person<Integer>(521);
System.out.println(integerPerson); // Person{s=521}
4.泛型的使用细节
- 泛型的
<>
里面只能是引用类型,不可以为基本数据类型 - 在给泛型指定具体类型后,可以传入该类型或者该类型的子类型
- 在实际的开发中,我们一般采用简写的泛型语法(去掉后面
<>
里面的内容),例如:
Person<Double> doublePerson = new Person<>(13.14);
- 如果不给泛型指定具体的数据类型,默认为
Object
类型🫗
5.自定义泛型类
泛型类的声明和非泛型类的声明类似,除了在类名后面添加了类型参数声明部分。
和泛型方法一样,泛型类的类型参数声明部分也包含一个或多个类型参数,参数间用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。因为他们接受一个或多个参数,这些类被称为参数化的类或参数化的类型。
- 使用泛型的数组,不能初始化
- 静态成员不能使用泛型,因为在类加载时,对象还没有创建,JVM无法完成其的初始化操作
接下来,我们看一个自定义泛型类的例子:
/**
* 自定义泛型类
*/
class Tiger<T,R,M> {
String name;
R r;
M m;
T t;
public Tiger(String name, R r, M m, T t) {
this.name = name;
this.r = r;
this.m = m;
this.t = t;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public R getR() {
return r;
}
public void setR(R r) {
this.r = r;
}
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
6.自定义泛型接口
- 在接口中,静态成员也不能使用泛型
- 泛型接口的类型,在继承接口或者实现接口的时候确定
- 没有指定类型,默认依然为
Object
类型
自定义泛型接口的例子:
/**
* 自定义泛型接口
*/
interface IUsb<U, R> {
R get(U u);
void hi(R r);
void run(R r1, R r2, U u1, U u2);
default R method(U u) {
return null;
}
}
这个自定义泛型的接口,如果有接口想要继承他,可以在继承接口时指定泛型接口的类型:
/**
* 继承接口时指定泛型接口的类型
*/
interface IA extends IUsb<String,Double>{
}
现在当我们实现IA接口时,会用String
和Double
类型替换我们接口原有的U和R类型:
例如:
/**
* 实现自定义的泛型接口
*/
class TTT implements IA {
@Override
public Double get(String s) {
return null;
}
@Override
public void hi(Double aDouble) {
}
@Override
public void run(Double r1, Double r2, String u1, String u2) {
}
}
当然,我们也可以在实现接口的同时指定接口的数据类型,例如:
/**
* 实现IUsb接口
*/
class III implements IUsb<Integer,String> {
@Override
public String get(Integer integer) {
return null;
}
@Override
public void hi(String s) {
}
@Override
public void run(String r1, String r2, Integer u1, Integer u2) {
}
}
7.自定义泛型方法
- 泛型方法,可以定义在普通类中,也可以定义在泛型类中
- 泛型方法在调用时,类型就确定了
使用代码示例:
public class AdvancedGenerics {
public static void main(String[] args) {
fly("保密",521);
fly(12,true);
}
/**
* 自定义泛型方法
*/
static public <T, R> void fly(T t, R r) {
System.out.println(t);
System.out.println(r);
}
}
---------------------------
输出:
保密
521
12
true
8.泛型通配符
泛型不具备继承性🧊
类型通配符一般是使用 ?
代替具体的类型参数。例如 List<?>
在逻辑上是List<String>,List<Integer>
等所有 List<具体类型实参>
的父类
/**
* 任意泛型类型
*/
public static void printCollection(List<?> c){
for (Object o : c) {
System.out.println(o);
}
}
<? extends T>
表示该通配符所代表的类型是T类型的子类。<? super T>
表示该通配符所代表的类型是T类型的父类。
/**
* ? extends 表示上限,如下代表可以接受AA或者AA的子类
*/
public static void printCollection2(List<? extends AA>c){
for (AA aa : c) {
System.out.println(aa);
}
}
/**
* ? super 表示下限,如下表示支持AA类以及AA类的父类,不限于直接父类
*/
public static void printCollection3(List<? super AA>c) {
for (Object o : c) {
System.out.println(o);
}
}