1. 什么是泛型
泛型,又称为“参数化类型”,是一种在不创建新类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型的方法。泛型的本质是参数化类型,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用时传入具体的类型(类型实参)。泛型在类、接口和方法中的使用,分别被称为泛型类、泛型接口、泛型方法。
2. 泛型只在编译期有效
Java中的泛型只在编译阶段有效。在编译过程中,正确校验泛型结果后,会将泛型的相关信息擦除,并且在对象进入和离开方法的边界处添加类型检查和类型转换的方法。也就是说,泛型信息不会进入到运行时阶段。下面是一个示例代码:
List<String> stringList = new ArrayList<>();
List<Integer> integerList = new ArrayList<>();
Class stringListClass = stringList.getClass();
Class integerListClass = integerList.getClass();
if (stringListClass.equals(integerListClass)) {
System.out.println("类型相同");
}
//输出
类型相同
通过上面的例子,可以说明在编译之后程序去泛型化。在运行时,stringList
和integerList
实际上是同一个类型。
3. 泛型的基本使用
泛型有三种使用方式:泛型类、泛型接口和泛型方法。
3.1 泛型类
泛型类允许我们定义一个类,其操作的数据类型由传递给类的类型参数决定。下面是一个泛型类的示例:
public class TestOne<T> {
private T key;
public TestOne(T key) {
this.key = key;
}
public T getKey() {
return key;
}
}
//test code
TestOne<String> test1 = new TestOne<>("aaa");
System.out.println(test1.getKey());
TestOne<Integer> test2 = new TestOne<>(2);
System.out.println(test2.getKey());
//输出
aaa
2
传入泛型参数后,编译器在传入实参的时候会进行类型检查,此时泛型才会发挥它真正的意义。泛型的类型参数只能是类类型,不能是基本类型。不能对泛型类型使用instanceof
操作,原因在于泛型擦除。
3.2 泛型接口
泛型接口与泛型类的定义及使用是基本一致的,泛型接口常用在各种类的生产器中,如AsyncTask
等。
//定义一个泛型接口
public interface Generator<T> {
public T next();
}
未传入泛型实参时,需要将泛型的声明延伸到类上
public class GeneratorImpl<T> implements Generator<T> {
@Override
public T show() {
return null;
}
}
传入了实参,则不需要在类上增加类型声明了
public class GeneratorImpl implements Generator<String> {
@Override
public String show() {
return null;
}
}
3.3 泛型方法
泛型方法是指在调用方法的时候指明泛型的具体类型:
public class TestTwo {
public <T> String show(T t) {
return t.toString();
}
}
TestTwo test = new TestTwo();
test.show(312);
test.show("卧槽");
只有声明了<T>
的方法才是泛型方法,所以可以在方法中使用泛型类型。静态方法无法访问类上定义的泛型,所以只能把泛型定义在方法上。
4. 通配符
通配符用于解决类型参数的不确定性问题。例如,下面的代码会编译报错,即使Integer
是Number
的子类:
private static void show(List<Number> genericity) {
System.out.println(genericity);
}
//use
List<Integer> numbers = new ArrayList<>();
//show(numbers); 编译报错
同一个泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。这时需要使用类型通配符(上下界通配符)来解决。
上界通配符? extends Number
:表示传入的类型实参必须是Number
或Number
的子类。 下界通配符? super Integer
:表示传入的类型实参必须是Integer
或是其父类。
ps: 当为泛型添加上下边界时,必须与泛型的声明在一起.
private <T extends Integer> T show(List<T> list) { return null; }
结论
Java泛型提供了一种强大的类型参数化机制,使得代码更加灵活和可重用。通过泛型,我们可以编写出类型安全的代码,同时避免了类型转换的繁琐。理解泛型的工作原理和使用方式,对于编写高质量的Java代码至关重要。
希望这篇文章能帮助您更好地理解和使用Java泛型。如果您有任何疑问或需要进一步的讨论,请在评论区留言。感谢您的阅读!