为什么要用泛型
Java语言引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率
为什么说Java泛型是假泛型,因为Java泛型只在在编译器生效(check类型检查),运行期被类型擦除,泛型还是转换成Object类型
泛型的声明
泛型类:public class Test<T>}{}
T表示未知类型
泛型接口:public interface Test<T>{}
和定义类一样
泛型方法:public <T> void Test(T name){}
- E (element) 元素 集合框架使用
- K V (key value) 分别代表java键值中的Key Value
- N 数字 主要用于表示数字
- T 通用类型
- S 通用类型
- U 通用类型
- V通用类型
泛型接口
/**
* @author zzy
* @createDate: 2022-02-08 16:31:55
* @description: 泛型接口1
*/
public interface GenericityInterface<T> {
//喜欢
T like( T like);
}
/**
* @author zzy
* @createDate: 2022-02-08 16:31:55
* @description: 泛型接口2
*/
public interface GenericityInterface2<T> {
//爱好
void hobby (T hobby);
}
泛型类
/**
* @author zzy
* @createDate: 2022-02-08 16:08:12
* @description: 泛型类
* 实现泛型接口的时候就声明类型GenericityInterface<String>,
* 要么创建类的时候声明类型 GenericityInterface2<S> -> g.hobby("飞");
*/
public class GenericityClass<T, S extends String > implements GenericityInterface<String>, GenericityInterface2<S> {
private T name;
public GenericityClass() {
}
public GenericityClass(T name) {
this.name = name;
}
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
//喜欢
@Override
public String like(String like) {
return "我喜欢吃"+like;
}
//爱好
@Override
public void hobby(S hobby) {
System.out.println(hobby + ":" + hobby.getClass().getName());
}
public static void main(String[] args) {
//声明泛型类型
GenericityClass<String, String> g = new GenericityClass<>("张三");
//喜欢
System.out.println(g.like("肉"));
//爱好
g.hobby("飞");
}
}
泛型方法
/**
* @author zzy
* @createDate: 2022-02-08 16:35:27
* @description: 泛型方法
*/
public class GenericityMethod {
//1.单个泛型
public <T> T getName1(T name) {
return name;
}
//2.多个泛型
//总的来说就是参数需要多少泛型,返回值前面就得定义几个泛型要不然编译期会出错
public <T, K, V> void getNameAndValueAndV(T name, K value, V v) {
}
//3.使用通配符
//3.1 <?> :无边界的通配符,能接收所有未知类型的泛型,参数必须是其它泛型类,类型不能复用,其它和方法1没区别
public void getName2(List<?> list) {
}
//3.2 <? extends Number> :上边界通配符,元素类型必须是Number的子类
public void getName3(List<? extends Number> name) {
}
//3.3 <? super Integer> :下边界通配符,元素类型必须是Integer的父类
public void getName4(List<? super Integer> name) {
}
}
泛型通配符
- <?> :无边界的通配符,能接收所有未知类型的泛型
- <? extends E> :上边界通配符,<? extends Number> :上边界通配符,元素类型必须是Number的子类
- <? super E> :下边界通配符,<? super Integer> :下边界通配符,元素类型必须是Integer的父类
泛型和通配符的使用
1.总是弄不清<T>和<?>
<T>是指这里可以接受一个类型,作为模板复用,T可以当作类型来在类或者方法的上下文中复用
public class GenericityClass<T, S extends String > implements GenericityInterface<String>, GenericityInterface2<S> {
private T name;
}
而<?>是在创建一个泛型类时不确定具体的类型,而是无限定类型
public static void main(String[] args) {
//1.<String, String> 确定泛型类型
GenericityClass<String, String> g = new GenericityClass<>("张三");
//2.<?, String> 第一个T依然不确定类型,符合get原则
GenericityClass<?, String g2 = new GenericityClass<>();
//可以get(),虽然类型是Object
Object name = g2.getName();
//不可以set,因为类型未知
//g2.setName("张三");
}
小结
<T>是创建泛型模板时使用,类上接口上方法上,告诉这里需要接受一个类型,相当于方法形参
<?>是初始化对象时使用,方法形参上或方法体中,告诉这里的类型具体是什么<?>就是无限定都可以,其实和String,Integer没什么区别,相当于方法实参
2.extends和super
-
extends 上边界通配符
-
super 下边界通配符
public static void main(String[] args) {
//3.<? extends Number, String >
GenericityClass<? extends Number, String > g3 = new GenericityClass<>();
//可以get
Number name1 = g3.getName();
//不可以set,因为类型未知
//g3.setName(1);
// 4.<? super Integer, String >
GenericityClass<? super Integer, String > g4 = new GenericityClass<>();
//get() 类型为Object
Object name2 = g4.getName();
//可以set,因为类型未知
g4.setName(1);
}
3.< T extends E> & < ? extends E> 和< T super E> & < ? super E>
这里其实和<T>与<?>区别不大,使用时机和使用地方都是一样的
关键是extends是上边界通配符,super 是下边界通配符,
缩小了泛型的范围;
,因为类型未知
g4.setName(1);
}
##### 3.< T extends E> & < ? extends E> 和< T super E> & < ? super E>
这里其实和与<?>区别不大,使用时机和使用地方都是一样的
关键是extends是上边界通配符,super 是下边界通配符,
缩小了泛型的范围;