什么是泛型?
为了确定数据添加的指定类型而加入,实际上就是一个类型占位符 如 T W E 都可以。 将代码中所指定的类型换成类型占位符 T W E 就是泛型。
泛型类
@Data
public class PersonT<T> {
private T name;
private T sex;
}
//泛型类测试
@Test
void personTTest() {
PersonT<String> stringPersonT = new PersonT<>();
stringPersonT.setName("张三");
System.out.println(stringPersonT.getName());
PersonT<Integer> integerPersonT = new PersonT<>();
integerPersonT.setName(111);
System.out.println(integerPersonT.getName());
}
泛型方法
public class PersonMethod<T> {
/**
* 泛型普通方法
*
* @param t
*/
public void say(T t) {
System.out.println(t + "说话了");
}
/**
* 静态泛型方法中的类型占位符和类中的占位符 毫无关系
* 必须在static后面定义 占位符
* 返回值为void
*/
public static <W> void staticVoidSay(W w) {
System.out.println(w + "说:这是一个静态泛型方法");
}
/**
* 静态泛型方法必须自己声明泛型<p>
* 返回值为 你传递进来的类型
*
* @param w 参数
* @param <W> 类型
* @return W
*/
public static <W> W staticWSay(W w) {
return w;
}
/**
* @param r 传递的值
* @param <R> 你想传递的类型
*/
public <R> void differentSay(R r) {
System.out.println(r + "说:这是一个与类泛型不同的普通泛型方法");
}
}
//泛型方法测试
@Test
void personMethodTest() {
PersonMethod<String> personMethod = new PersonMethod<>();
//普通泛型方法 与类的泛型类型一致
personMethod.say("小明");
//普通泛型方法 与类的泛型类型不一致
personMethod.differentSay(2);
//无返回值 静态泛型方法 直接调用即可
PersonMethod.staticVoidSay("小黄");
//有返回值 静态泛型方法
int i = PersonMethod.staticWSay(1);
System.out.println(i);
}
泛型接口
//接口
public interface PersonInterface<T> {
/**
* 普通泛型接口 与泛型接口的类型一致
*
* @param t
*/
void say(T t);
}
//普通实现
@Service
public class PersonInterfaceStringImpl implements PersonInterface<String> {
@Override
public void say(String o) {
System.out.println(o + "说话了");
}
}
//泛型实现
@Service
public class PersonInterfaceTImpl<T> implements PersonInterface<T> {
@Override
public void say(T t) {
System.out.println(t + ",未指定类型 在实现中定义了类型");
}
}
//测试
@Test
void PersonInterfaceImplTest() {
//泛型接口的实现类可以指定具体的泛型接口的具体泛型类型
PersonInterface<String> string = new PersonInterfaceStringImpl();
string.say("小绿");
//泛型接口的实现类 如果没有指定具体的泛型类型,必须要在这个实现类中声明一个类型占位符给接口使用
PersonInterface t = new PersonInterfaceTImpl();
t.say(2);
t.say("小黄");
}
擦除模式
public class PersonEraseMode<T> {
private T name;
public void say(){
System.out.println("擦除模式的say");
}
}
//擦除模式测试
@Test
void PersonEraseModeTest() {
PersonEraseMode<String> eraseModeString = new PersonEraseMode<>();
PersonEraseMode<Integer> eraseModeInt = new PersonEraseMode<>();
//条件始终为true
System.out.println(eraseModeString.getClass() == eraseModeInt.getClass());
/**
* Java类编译之后成为字节码文件
* 泛型类 Person<T> 到字节码阶段时 会将泛型类型去除 成为 Person person = new Person();
* 运行期间会将 泛型类型给擦除掉
*
* 编码编译阶段
* PersonEraseMode<String> eraseModeString = new PersonEraseMode<>();
* PersonEraseMode<Integer> eraseModeInt = new PersonEraseMode<>();
*
* 运行阶段
* PersonEraseMode eraseModeString = new PersonEraseMode();
* PersonEraseMode eraseModeInt = new PersonEraseMode();
* 将泛型类型给去除掉,所以他们两的class字节码是完全相同的
*
*/
}
通配符 ? :由于Java中的继承关系,在泛型中不做任何的声明修饰情况写下是不被认可的,所以需要使用通配符处理。
使用通配符在泛型中将Java中的继承关系 重写绑定
通配符使用 ? 表示 ?是泛型中所有类的父类 Object
@Data
public class PersonWildcard<T> {
private T name;
public void say(PersonWildcard<T> personWildcard) {
this.setName(personWildcard.getName());
System.out.println("这是无法识别Java继承关系的的say:" + name);
}
}
//测试
@Test
void personWildcardTest() {
PersonWildcard<Number> numberPersonWildcard = new PersonWildcard<>();
PersonWildcard<Integer> integerPersonWildcard = new PersonWildcard<>();
//即便 Integer和Number 是父子关系,但是泛型当中它是无法识别的
//numberPersonWildcard.say(integerPersonWildcard);
}
由于无法识别 所以需要使用 通配符来解决一下
/**
* ? 通配符 表示 任何类型 Object
*
* @param personWildcard 参数
*/
public void sayWildcardObj(PersonWildcard<?> personWildcard) {
//强制转换
this.setName((T) personWildcard.getName());
System.out.println("这是任意通配符?的say:" + name);
}
//测试
@Test
void personWildcardTest() {
PersonWildcard<Number> numberPersonWildcard = new PersonWildcard<>();
PersonWildcard<Integer> integerPersonWildcard = new PersonWildcard<>();
//即便 Integer和Number 是父子关系,但是泛型当中它是无法识别的
//numberPersonWildcard.say(integerPersonWildcard);
integerPersonWildcard.setName(1111);
//将它换为通配符?即可实现继承关系
numberPersonWildcard.sayWildcardObj(integerPersonWildcard);
//但是 不是继承关系也可以 因为 通配符?表示Object类型
PersonWildcard<String> stringPersonWildcard = new PersonWildcard<>();
stringPersonWildcard.setName("小明");
numberPersonWildcard.sayWildcardObj(stringPersonWildcard);
}
虽然通配符可以实现继承关系,但是 同时,即使不是继承关系 通配符也可以绑定,因为 通配符 ? 表示任意类型 类似于 Object
所以如果需要限制类型,则可使用 上边界 extends、下边界 super 来实现
上边界
/**
* 上边界
* <p>
* 传递进来的值 必须为 特定类型的子类 或者 特定类型本身
* <p>
* ? extends T 传递进来的类型 必须 继承 T
*
* @param personWildcard 本身类型 or 子类类型
*/
public void sayWildcardExtends(PersonWildcard<? extends T> personWildcard) {
this.setName(personWildcard.getName());
System.out.println("这是上边界 指定泛型类型 指定泛型可以传入 T 和 T的子类:" + name);
}
//测试
@Test
void test(){
PersonWildcard<Number> numberPersonWildcard = new PersonWildcard<>();
PersonWildcard<Integer> integerPersonWildcard = new PersonWildcard<>();
PersonWildcard<String> stringPersonWildcard = new PersonWildcard<>();
//上边界 限制 Number 类型的子类 或者是Number类型本身
numberPersonWildcard.sayWildcardExtends(integerPersonWildcard);
//String不是Number类型的子类,所以不可传递
//numberPersonWildcard.sayWildcardExtends(stringPersonWildcard);
}
下边界
/**
* 下边界
* 传递进来的值 必须为 特定类型的父类 或者 特定类型本身
* ? super T 传递进来的类型 必须是T的父类
*
* @param personWildcard 必须是T或者T的父类
*/
public void sayWildcardSuper(PersonWildcard<? super T> personWildcard) {
this.setName((T) personWildcard.getName());
System.out.println("这是下边界 泛型类型必须是T 或者T的父类:" + name);
}
//测试
@Test
void test(){
PersonWildcard<Number> numberPersonWildcard = new PersonWildcard<>();
PersonWildcard<Integer> integerPersonWildcard = new PersonWildcard<>();
//下边界 限制Number类型 或者Number的父类
numberPersonWildcard.sayWildcardSuper(numberPersonWildcard);
//Integer不是Number的父类,所以不能船体
//numberPersonWildcard.sayWildcardSuper(integerPersonWildcard);
}
什么时候用上边界? 什么时候用下边界?
上边界:在读取T这个类型数据的时候,但 不用写入、修改 (只读取、不编辑)
下边界:需要写入数据,但 不需要读取 (只写入、不读取)