文章目录
- 什么是泛型
- 泛型的相关概念
- 泛型的作用
- 泛型的使用
- 泛型类语法
- 泛型接口语法
- 泛型方法语法
- 泛型类的简单示例
- 泛型接口的简单示例
- 基于泛型的简单工厂方法
- 泛型的上界与下界
- 泛型的一些使用建议
什么是泛型
从JDK1.5开始引入泛型(generic)语法。对类型实现了参数化,编辑器根据上下文推导出实参类型,省略了实参类型的填写。
泛型的相关概念
术语准备
术语 | 范例 | 说明 |
---|---|---|
参数化的类型 | List<String> | 表示List中元素的类型为String的容器 |
实际类型参数 | String | 泛型使用时的实参 |
泛型 | List<E> | 声明中具有一个或者多个类型参数的类或接口即泛型 |
形式类型参数 | E | 泛型声明中的形参 |
原生态类型 | List | Raw Type。即没有使用泛型,主要是为了兼容之前大量没有使用泛型的代码。 |
有限制类型参数 | <E extends Number> | 只接受 Number 的子类型或者 Number 本身作为 E 的类型实参。 范围上界 |
递归类型限制 | <T extends Comparable<T>> | 只接收实现了Comparable接口的。范围上界 |
有限制通配符类型 | List<? extends Number> | 只接受Number 或其子类类型。范围上界 |
无限制通配符类型 | List<?> | 可以接收任意类型 |
类型令牌 | String.class | 类的字面用在方法传递时的称呼 |
泛型方法 | static <E> List<E> aslist(E[] a) |
泛型的编译时进行泛型擦除
程序编写的时候可以指定了泛型的元素类型为String类型,在编译的时候就变成Object。
擦除机制,在编译的过程中,将泛型转换(所有类型)为Object的机制。
泛型的作用
- 加强类型安全检查:存放元素时,指明容器接收的对象的类型,让编译器进行类型安全检查
- 无需手动进行类型转换,加快运行效率:取出元素时,无需手动进行类型转换,加快运行效率
进行类型安全检查之后,会在编译过程中反馈错误,减少运行时的错误。
泛型的使用
泛型常用的形参大写字母
E
表示 ElementK
表示 KeyV
表示 ValueN
表示 NumberT
表示 TypeS
,U
,V
- 第二、第三、第四个类型
泛型类语法
class 类名称 <泛型标识、泛型标识,...> {
private 泛型标识 变量名;
......
}
泛型接口语法
interface 接口名称 <泛型标识,泛型标识,...>{
泛型标识 方法名();
}
泛型方法语法
方法限定符 <类型形参列表> 返回值类型 方法名称(形参列表){
}
泛型类的简单示例
// 定义泛型类
public class GenericClass<T, N> {
private T value1;
private N value2;
// 使用泛型类型作为形参类型和返回值
public T getValue1() {
return value1;
}
public void setValue1(T value1) {
this.value1 = value1;
}
public N getValue2() {
return value2;
}
public void setValue2(N value2) {
this.value2 = value2;
}
public static void main(String[] args) {
GenericClass<Integer, String> s1 = new GenericClass<>();
s1.setValue1(1);
s1.setValue2("XX");
System.out.println(s1.getValue1() + ":" + s1.getValue2());
GenericClass<Double, String> s2 = new GenericClass<>();
s2.setValue1(1.1);
s2.setValue2("YY");
System.out.println(s2.getValue1() + ":" + s2.getValue2());
}
}
泛型接口的简单示例
在接口中定义的类型参数可以在接口中当做类型使用,任何需要类型的地方都可以使用类型参数替代
// 定义泛型接口的类型参数为T
public interface Humans<T> {
// 使用类型参数T作为方法参数类型
void say(T t);
// 使用类型参数T作为返回值类型
T get();
}
// 实现泛型接口的时候传入类型
public class BlackHuman implements Humans<BlackHuman> {
public final String name;
public BlackHuman(String name) {
this.name = name;
}
@Override
public void say(BlackHuman yellowHuman) {
System.out.println("我叫" + this.name + ",是黑种人");
}
@Override
public BlackHuman get() {
return this;
}
}
public class Demo {
public static void main(String[] args) {
BlackHuman blackHuman = new BlackHuman("乔丹");
blackHuman.say(blackHuman);
System.out.println(blackHuman.get().name);
}
}
基于泛型的简单工厂方法
public interface IService {
String getServiceName();
}
public class MyService1 implements IService {
@Override
public String getServiceName() {
return "My Service1.";
}
}
public class MyService2 implements IService {
@Override
public String getServiceName() {
return "My Service2.";
}
}
package com.donny.demo;
/**
* @author 1792998761@qq.com
* @description
* @date 2023/10/8
*/
public class ServiceFactory {
private ServiceFactory() {
}
// 简单写法-通过额外参数表名服务
public static IService getService(String key) {
if ("MyService1".equals(key)) {
return new MyService1();
} else if ("MyService2".equals(key)) {
return new MyService2();
} else {
return null;
}
}
// 使用泛型方法
public static <T> T getInstance(Class<T> className) {
T result = null;
try {
result = className.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
return result;
}
}
public class Demo {
public static void main(String[] args) {
IService service1 = ServiceFactory.getInstance(MyService1.class);
IService service2 = ServiceFactory.getInstance(MyService2.class);
System.out.println(service1.getServiceName());
System.out.println(service2.getServiceName());
}
}
泛型的上界与下界
由于java中继承的特性
上界语法
class 类名称 <类型标识 extends 类型边界> {
......
}
类型边界可以是类,也可以是接口。若类型边界是类,那么泛型中的类型只能接受边界类本身或其子类。若类型边界是接口,那么泛型中的类型必须实现了边界接口。
// 传入的类型需要是Number或Number的子类
public interface Price<? extends Number> {
}
// 传入的类型需要实现Comparable接口
public class ApplicableTransition<? extends Comparable<E>> {
}
下界语法
class 类名称 <类型标识 super 类型边界> {
......
}
类型边界可以是类,也可以是接口。若类型边界是类,那么泛型中的类型只能接受边界类本身或其父类。
泛型的一些使用建议
-
不使用原生态类型
-
消除非受检警告
可以在类,方法,对象声明上增加注解来消除非受检个告警
@SuppressWarnings("unchecked")
-
使用泛型时,列表优先于数组
-
利用有限通配符提升api的灵活性
-
优先考虑类型安全的异构容器