1.泛型类的定义
泛型的实质就是一个类型占位符(T W E均可以代替),或指定其类型,如String Integer等。
List源码中占位符如下图:
image.png
不知道传什么类型,用泛型来占位。
List后就是一个泛型,E表示占位,即传什么类型就是什么类型。
image.png
例如:E的位置传的是string,那就是string类型,后面的泛型可写可不写,因为会根据前面指定的泛型自动匹配。
image.png
泛型的类型传string后,调用add方法只能add String类型
image.png
泛型的类型传Integer后,调用add方法只能add Integer类型
image.png
故:泛型类型传什么类型,就只能使用什么类型。
泛型是为了确定数据添加的指定类型而加入的。
package com.example.fxtest;
/**
* @author zwp
* @description: 确定属性的类型时可直接给定
*/
public class Person {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public class Test {
public static void main(String[] args) {
Person person = new Person();
person.setName("zhangsan");
System.out.println(person.getName());
}
}
运行结果:zhangsan
泛型类:在不知道未来属性类型时,可以使用类型占位符来占位,这时定义一个泛型类,即把代码中所有特定类型换成类型占位符。
package com.example.fxtest;
/**
* @author zwp
* @description: 如果说在不知道未来name和address类型的时候,可以使用类型占位符来进行占位
* 定义一个泛型类
* 定义泛型类实质就是将现在代码中所有的特定类型换成类型占位符
*/
public class FxPerson<T> {
private T name;
private T address;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public T getAddress() {
return address;
}
public void setAddress(T address) {
this.address = address;
}
}
public class Test {
public static void main(String[] args) {
FxPerson<String> fxPerson = new FxPerson<>();
fxPerson.setName("lisi");
System.out.println(fxPerson.getName());
}
}
运行结果:lisi
泛型类型传递什么类型,使用的时候只能用什么类型。如下图所示:
image.png
image.png
多个类型的泛型类:
package com.example.fxtest;
/**
* @author zwp
* @description: 传多个参数泛型类
*/
public class FxPersonMore<K,V> {
private K name;
private V address;
public K getName() {
return name;
}
public void setName(K name) {
this.name = name;
}
public V getAddress() {
return address;
}
public void setAddress(V address) {
this.address = address;
}
}
public class Test {
public static void main(String[] args) {
//多个类型的泛型类
FxPersonMore<String,Integer> fxPersonMore = new FxPersonMore<>();
fxPersonMore.setName("wangwu");
fxPersonMore.setAddress(123);
System.out.println(fxPersonMore.getAddress());
System.out.println(fxPersonMore.getName());
}
}
运行结果:123
wangwu
传递什么类型,使用什么类型:
image.png
2.泛型方法
2.1 泛型普通方法
给类指定一个泛型,通过类的泛型确定方法参数的泛型
package com.example.fxtest;
/**
* @author zwp
* @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
*/
public class FxPersonM<T> {
public void show(T name){
System.out.println(name+"正在演讲!");
}
}
package com.example.fxtest;
/**
* @author zwp
* @description: 泛方法
*/
public class TestFxMethod {
public static void main(String[] args) {
FxPersonM<String> fxPersonM = new FxPersonM<>();
fxPersonM.show("heiliu");
}
}
运行结果:heiliu正在演讲!
2.2 泛型静态方法
静态泛型方法中的类型占位符和类中的泛型占位符是没有关系的,如下图:
image.png
未经过标识的占位符不可使用,如下图
静态方法中的占位标识符在使用前必须经过标识,如下图:
image.png
泛型静态方法示例:
package com.example.fxtest;
/**
* @author zwp
* @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
*/
public class FxPersonM<T> {
/**
* 2.泛型静态方法
* 静态泛型方法中的类型占位符和类中的泛型占位符是没有关系的
* @param name
*/
public static<W> void show1(W name){
System.out.println(name+":静态方法正在演讲!");
}
}
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型方法
*/
public class TestFxMethod {
public static void main(String[] args) {
// 2.泛型静态方法
FxPersonM.show1("lisa");
FxPersonM.show1(123);
}
}
运行结果:
lisa:静态方法正在演讲!
123:静态方法正在演讲!
有返回值的静态泛型方法:
package com.example.fxtest;
/**
* @author zwp
* @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
*/
public class FxPersonM<T> {
/**
* 有返回值类型的静态泛型方法占位符
* @param name
* @param <E> 占位符
* @return
*/
public static<E> E show2(E name){
return name;
}
}
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型方法
*/
public class TestFxMethod {
public static void main(String[] args) {
//有返回值的静态泛型方法
System.out.println(FxPersonM.show2("David"));
}
}
运行结果:David
3.泛型方法补充说明:
如果你想让普通方法的类型不与类方法的类型一致,该怎么实现呢?
例如:类方法的泛型占位符为外部传入的任意类型,但普通方法只想要用String类型
只要是方法,就可以自定义泛型占位符
package com.example.fxtest;
/**
* @author zwp
* @description: 给类指定一个泛型,通过类的泛型确定方法参数的泛型
*/
public class FxPersonM<T> {
//3.普通方法自定义占位符
public <W> void selfShow(W name) {
System.out.println(name + "正在演讲!");
}
}
image.png
fxPersonM.selfShow("alic");
fxPersonM.selfShow(45);
运行结果:
alic正在演讲!
45正在演讲!
4.泛型接口
定义一个泛型接口,不指定具体泛型类型
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型接口
*/
public interface PerInterface<T>{
public void show(T name);
}
定义一个实现泛型接口的类;实现泛型接口,将泛型接口中占位符指定为某一类型。
package com.example.fxtest;
/**
* @author zwp
* @description: 实现泛型接口的类
* 1.实现泛型接口,将泛型接口中占位符指定为某一类型
* 2.泛型接口的实现类可以指定具体的泛型接口的具体泛型的类型
*/
public class PerIntImpl implements PerInterface<String>{
@Override
public void show(String name) {
System.out.println(name);
}
}
测试实现泛型接口的类
package com.example.fxtest;
/**
* @author zwp
* @description: 测试实现泛型接口的类
*/
public class TestInterface {
public static void main(String[] args) {
PerIntImpl perInt = new PerIntImpl();
perInt.show("li");
}
}
运行结果:li
定义一个实现泛型接口的类;实现泛型接口,不指定泛型类型。
//泛型接口的实现类如果没有指定具体的泛型,必须要在这个实现类中声明一个泛型给泛型类型
//这种情况外部调用show方法可以传任意类型
public class PerIntImpl<T> implements PerInterface<T>{
@Override
public void show(T name) {
System.out.println(name);
}
}
5.泛型擦除模式
定义一个泛型类
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型擦除模式
*/
public class FxPerErase<T> {
private T name;
public void show(){
System.out.println("方法被调用");
}
}
测试类:
package com.example.fxtest;
/**
* @author zwp
* @description: 擦除模式
*/
public class TestEras {
public static void main(String[] args) {
FxPerErase<String> fxPerErase = new FxPerErase<>();
FxPerErase<String> fxPerErase1 = new FxPerErase<>();
System.out.println(fxPerErase.getClass()==fxPerErase1.getClass());
}
}
运行结果:true
如果把fxPerErase1前面的类型变成Integer,结果还是true。
即:
FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();
此时就涉及到一个名词叫擦除模式
Java中的泛型只存在于编码编译阶段,在运行期间泛型的类型是会被去除掉的。
擦除模式实质就是在代码运行期间将所有的泛型全部去掉
Ques:为什么要使用擦除模式?
Ans:为了兼容jdk老版本的编码
6.泛型通配符
(Integer是Number的子类)
image.png
Java中的继承并不是泛型中的继承
Java中的父子类关系 在泛型中 并不是父子类关系,示例如下图所示:
image.png
解决上述问题,使用通配符
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型擦除模式
*/
public class FxPerErase<T> {
private T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
public void show(FxPerErase<?> fxPerErase){
this.setName((T)fxPerErase.getName());
}
}
image.png
package com.example.fxtest;
/**
* @author zwp
* @description: 擦除模式
* 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
* 会使用通配符在泛型中将java中的继承关系 重新绑定
* 通配符一般使用?来表示 可以理解为?是泛型中所有类的父类
* 通配符的上边界 下边界问题
* java中的继承 并不是泛型中的继承
* java中的父子类关系 在泛型中 并不是父子类关系
*/
public class TestEras {
public static void main(String[] args) {
FxPerErase<Number> fxPerErase = new FxPerErase<>();
FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();
FxPerErase<String> fxPerErase2 = new FxPerErase<>();
fxPerErase1.setName(111);
fxPerErase.show(fxPerErase1);//fxPerErase fxPerErase1 fxPerErase2均可
System.out.println(fxPerErase.getName());
}
}
运行结果:111
7.泛型上边界 extends 下边界 super 关键字
7.1.? extends T 代表的是泛型可以传入T和T的子类的类型
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型擦除模式
*/
public class FxPerErase<T> {
private T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
//? extends T 代表的是泛型可以传入T和T的子类的类型
public void show(FxPerErase<? extends T> fxPerErase){
this.setName((T)fxPerErase.getName());
}
}
package com.example.fxtest;
/**
* @author zwp
* @description: 擦除模式
* 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
* 会使用通配符在泛型中将java中的继承关系 重新绑定
* 通配符一般使用?来表示 可以理解为?是泛型中所有类的父类
* 通配符的上边界 extends 下边界问题 super 关键字
* java中的继承 并不是泛型中的继承
* java中的父子类关系 在泛型中 并不是父子类关系
*/
public class TestEras {
public static void main(String[] args) {
FxPerErase<Number> fxPerErase = new FxPerErase<>();
FxPerErase<String> fxPerErase2 = new FxPerErase<>();
fxPerErase2.setName("2222");
fxPerErase.show(fxPerErase2);//fxPerErase fxPerErase1 fxPerErase2均可
System.out.println(fxPerErase.getName());
}
}
运行结果:2222
7.2.? super T 代表的是泛型可以传入T和T的父类
package com.example.fxtest;
/**
* @author zwp
* @description: 泛型擦除模式
*/
public class FxPerErase<T> {
private T name;
public T getName() {
return name;
}
public void setName(T name) {
this.name = name;
}
//? super T 代表的是泛型可以传入T和T的父类
public void show(FxPerErase<? super T> fxPerErase){
//System.out.println("方法被调用");
this.setName((T)fxPerErase.getName());
}
}
package com.example.fxtest;
/**
* @author zwp
* @description: 擦除模式
* 通配符:由于java中继承关系,在泛型中不做任何声明修饰的情况下是被认可的,所以要使用通配符来进行处理
* 会使用通配符在泛型中将java中的继承关系 重新绑定
* 通配符一般使用?来表示 可以理解为?是泛型中所有类的父类
* 通配符的上边界 extends 下边界问题 super 关键字
* java中的继承 并不是泛型中的继承
* java中的父子类关系 在泛型中 并不是父子类关系
*/
public class TestEras {
public static void main(String[] args) {
FxPerErase<Number> fxPerErase = new FxPerErase<>();
FxPerErase<Integer> fxPerErase1 = new FxPerErase<>();
FxPerErase<String> fxPerErase2 = new FxPerErase<>();
fxPerErase1.setName(111);
fxPerErase2.setName("2222");
fxPerErase.show(fxPerErase);//super 下边界
System.out.println(fxPerErase.getName());
}
}
7.3.什么时候用上边界 什么时候用下边界
1.上边界 在读取T这个类型数据的时候,但不写入数据的时候使用上边界
2.下边界 需要写入数据的时候,但不需要读取的时候使用下边界
3.既要读又要写不使用通配符
网友评论