赞
踩
目录
泛型即——“参数化类型”
它允许我们定义一个类、接口或方法,使它们可以接受指定类型的参数,从而提高了代码的复用性和安全性。
泛型定义在类上的时候,整个类都可以使用泛型(除了静态的)
比如:List、Set、Map..
- class 类名 <泛型标识符>{
-
- }
- public class Student<T, U> {
- private T name;
- private U age;
- public Student(T name, U age) {
- this.name= name;
- this.age= age;
- }
- public T getName() {
- return name;
- }
- public U getAge() {
- return age;
- }
- public void setName(T name) {
- this.name= name;
- }
- public void setAge(U age) {
- this.age= age;
- }
- }
在上面的代码中,我们定义了一个泛型类Student <T, U> ,其中T,和U是类型参数。Student类,第一个值的类型是T,第二个值的类型是U。
在Student类的构造函数和方法中,我们可以使用类型参数T和U,从而实现了通用性。
例如:
- Student<String, Integer> student= new student<>("BroRiver", 20);
- String s = student.getName();
- Integer i = student.getAge();
在上面的代码中,我们创建了一个Student <String, Integer>对象,表示该对中的第一个值是字符串类型,第二个值是整数类型。
get我们可以使用get方法来获取该对中的值,并在不需要进行类型转换的情况下使用它们。
自定义泛型类可以让我们在定义类时指定类型参数,从而实现通用性和可重用性。
1、泛型的类型参数只能是类类型,不能是基本数据类型
2、不能对加泛型的类类型使用instanceof操作,编译时不会通过
- interface 接口名<标识>{
-
- }
定义泛型接口
- public interface MyInterface<T>{
- public T next();
- }
1.未传入泛型实参
- //通用类
- public class MyGenericClass<T> implements MyInterface<T> {
- @Override
- public T next() {
- return null;
- }
- }
2.传入泛型实参
- //指定String
- public class MyStringClass implements MyInterface<String> {
- @Override
- public String next() {
- return “hello world”;
- }
- }
类实现了一个泛型接口,在类名后面传入了一个具体的泛型实参String
泛型方法要比前面两个更复杂
首先我们要清楚一点:【泛型方法和泛型类当中的方法,是不一样的概念】
1、只有当方法中使用了泛型类型参数时,该方法才是泛型方法
2、泛型类或泛型接口中的方法,是没有定义泛型的,它们是使用定义在类或接口上的范型。
3、普通类可以定义泛型方法;泛型类也可以定义泛型方法,但是要换一个标识。
- 修饰符 <T> 返回类型 方法名(形参列表){
-
- }
只有声明了<T>的方法才是泛型方法
- public class ArrayUtils {
- public static <T> void printArray(T[] array) {
- for (T element : array) {
- System.out.print(element + " ");
- }
- System.out.println();
- }
- }
在上面的示例中,printArray()
是一个泛型方法,它接受一个泛型数组作为参数,并将数组中的元素打印出来。<T>
是类型参数列表,用于定义泛型类型参数,它可以在方法的返回值类型、参数类型等位置使用。在方法内部,我们使用了for
循环遍历数组,并使用System.out.print()
方法打印每个元素。
泛型方法,可以接受不同类型的数组作为参数,从而实现了代码的重用。
1、可以一次性定义多个泛型标识,中间用逗号分隔 形如:<K,V,T>
2、可以指定传入的范型的边界,比如它是谁的子类; 形如:<T extends 父类> 当我们这么做的时候,就可以在代码中用T的对象,调用父类的可见方法了。
3、也可以指定多个边界,比如T必须继承谁,同时要实现哪些接口。 语法:<T extends 父类 & 接口1 & 接口2>
注意:
如果要指定T的父类是谁,只能写一个,且是第一个; 如果要指定T的接口,不指定父类,那么顺序无所谓。
1、在给泛型指定数据类型的时候,不能是基本数据类型;
2、如果在使用的时候没有给泛型指定数据类型,那么它会默认为Object类型;
3、JDK11中不能使用 instanceof 判断 某个对象是否属于某个类的带泛型类型;JDK17允许了。
gc instanceof GenericClass<String>
4、类型推断【当我们调用一个泛型方法时,如果方法的参数类型是一个泛型类型,那么编译器就可以根据该参数的实际类型推断出泛型类型】
5、在使用泛型的时候,还有一种特殊语法叫做:通配符与上下边界。
Java的泛型被称为“伪泛型”,说白了它就是一个语法糖,只能影响编译器(无法影响运行期)。
泛型相对于Java虚拟机来说是透明的,编译器编译后生成的二进制代码有没有泛型都是一样的。
类型擦除
类型擦除是指在编译器编译泛型代码时,将所有 泛型类型 替换成其 对应的边界类型 或Object类型,从而生成字节码文件。
这样做是为了保持Java语言的向后兼容性,因为泛型是在Java 5中引入的,而Java 5之前的版本并不支持泛型。
类型擦除的结果是,在运行时无法访问泛型类型的具体信息,例如泛型类型参数的实际类型、边界类型等。
示例:
- public class GenericClass<T> {
- private T value;
- public void setValue(T value) {
- this.value = value;
- }
- public T getValue() {
- return value;
- }
- }
在上面的示例中,GenericClass 是一个泛型类,它的类型参数为T。
在编译时,由于类型擦除的原因,T将被替换成Object类型,所以实际上value字段的类型是Object,而get、set方法的参数和返回值类型也是Object。这意味着,无论我们在运行时将GenericClass 实例化成什么样的类型,它们都将具有相同的字节码,只是字段和方法的参数和返回值的类型不同。这也是类型擦除的一个副作用,即无法在运行时访问泛型类型参数的具体信息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。