目录
前言:
😉Java和其他语言一样,都支持泛型,包括泛型类和泛型方法。泛型的出现使得类型参数化,Java提出了擦除机制,如果没有声明上界class Test<T>,最终将Test类都擦除Object类。如果声明上界class Test<T extends Number>意味着只能传递Number类或者Number的子类,最终擦除成Number类。这些动作都是在编译期间完成的,编译时会自动进行类型检查和转换,运行时没有泛型的概念。
泛型类的使用
class Test<T>{
private T[] tmp = (T[])new Object[10];
public void setTmp(T tmp, int pos) {
this.tmp[pos] = tmp;
}
public T getTmp(int pos) {
return tmp[pos];
}
}
public class Test17 {
public static void main(String[] args) {
Test<Integer> test = new Test<>();
test.setTmp(10,0);
int a = test.getTmp(0);
Test<String> test2 = new Test<>();
test2.setTmp("aaa",0);
String b = test2.getTmp(0);
System.out.println(a + " " + b);
}
}
😆泛型类中是不可以直接new数组(T[] arr = new T[10]),因为在擦除机制中会将T擦成Object类,而Object数组可以存放存放多种类型数据,编译器认为这是不安全的,直接在编译期间就阻止了。T[] tmp = (T[])new Object[10]这样的写法编译器也会报警告,毕竟Object数组不是T类型的数组,除非T刚好擦除成Object。
😆test.setTmp(10,0) test2.setTmp("aaa",0) 在编译期间会根据Integer和String进行类型检查,直接阻挡在编译期间。int a = test.getTmp(0) String b = test2.getTmp(0) 就是自动拆包的过程。
擦除机制
🎉通过javap -c查看字节码文件,可以清楚看见将T擦除为Object类
泛型上界
class Test<T extends Number>{
private T[] tmp = (T[])new Object[10];
public void setTmp(T tmp, int pos) {
this.tmp[pos] = tmp;
}
public T getTmp(int pos) {
return tmp[pos];
}
}
public class Test17 {
public static void main(String[] args) {
Test<Integer> test = new Test<>();
test.setTmp(10,0);
int a = test.getTmp(0);
}
}
🎄可以指定泛型类的上界,同时也就限制了传递参数的类型,只能传递Number的子类,编译时会将T擦除成Number,否则不能通过编译 。
静态泛型方法
class Test{
public static<T extends Comparable<T>> void fun(T a, T b) {
if(a.compareTo(b) > 0) {
System.out.println(a);
}else {
System.out.println(b);
}
}
}
public class Test17 {
public static void main(String[] args) {
Integer a = 10;
Integer b = 20;
Test.fun(a, b);
}
}
🐵静态泛型方法不依赖于对象,直接通过类名就可以调用,因此类型的传参是体现在方法中,当使用者调用方法时,在编译期间会进行类型检查和转换。擦除机制和前面擦除机制是一致的。
非静态泛型方法
class Test{
public <T extends Comparable<T>> void fun(T a, T b) {
if(a.compareTo(b) > 0) {
System.out.println(a);
}else {
System.out.println(b);
}
}
}
public class Test17 {
public static void main(String[] args) {
Integer a = 10;
Integer b = 20;
Test test = new Test();
test.fun(a, b);
}
}
🧢非静态泛型方法就得依赖对象,直接通过对象调用即可,其他和静态泛型方法一致。
通配符
🪖在使用泛型类实例化对象后,这个被作为参数传递时,由于泛型对象中可能会有多种不同类型,那么形参类型就无法确定。?符号就解决了这个问题,可以接收任何类型的参数。同时也可以指定通配符的下界和上界。
通配符上界
class Test<T> {
private T date;
public void setDate(T date) {
this.date = date;
}
public T getDate() {
return this.date;
}
}
public class Test17 {
public static void fun(Test<? extends Number> tmp) {
Number a = tmp.getDate();
System.out.println(a);
}
public static void main(String[] args) {
Test<Integer> test = new Test<>();
test.setDate(10);
fun(test);
}
}
🎈擦除机制会将tmp类型擦除成Number,因此只能传递Number的子类。由于tmp类型会有多种,所以不可以在fun中设置成员变量的值(date)。可以获取date,它们都有一个共同的父类。
通配符下界
class Test<T> {
private T date;
public void setDate(T date) {
this.date = date;
}
public T getDate() {
return this.date;
}
}
public class Test17 {
public static void fun(Test<? super String> tmp) {
tmp.setDate("aaa");
}
public static void main(String[] args) {
Test<String> test = new Test<>();
fun(test);
System.out.println(test.getDate());
}
}
😯指定通配符下界,只能传递String类的父类,由于父类会有多种,不能获取到tmp中成员变量(date),因为获取后无法指定一个类型去接收。可以设置date,tmp是String或者String的父类。
小结:
🐵泛型概念是在Java5后出现,使得代码更加灵活,需深入理解,使我们写代码更加有设计感。