Java泛型
泛型
泛型就是指在类定义时不会设置类中的属性或方法参数的具体类型,而是在类使用时(创建对象)再进行类型的定义。会在编译期检查类型是否错误
- 类型安全
- 消除强制类型转换
泛型类
- 语法:
class 类名称 泛型标识,泛型标识,……> { private 泛型标识 变量名; …… }
常用的 泛型标识:T、E、K、V
- 使用语法:
类名具体的数据类型> 对象名 = new 类名具体的数据类型> ();
Java1.7以后,后面的> 中的具体的数据类型可以省略不写
类名具体的数据类型> 对象名 = new 类名> ();
- MainClass类
package itiheima_09; //泛型类 public class MainClassT> { //定义成员变量,T:是由外部使用类指定 private T key; //get、set方法 public T getKey() { return key; } public void setKey(T key) { this.key = key; } //构造方法 public MainClass(T key) { this.key = key; } public MainClass() { } //toString方法 @Override public String toString() { return "MainClass{ " + "key=" + key + '} '; } }
- MainDemo类
package itiheima_09; public class MainDemo { public static void main(String[] args) { MainClassString> stringMainDemo = new MainClass> ("hello"); String key1 = stringMainDemo.getKey(); System.out.println(key1); //hello MainClassInteger> intMainDemo = new MainClass> (100); int key2 = intMainDemo.getKey(); //包装类类型装换为基本数据类型:拆箱 System.out.println(key2); //100 //泛型类在创建对象的时候,没有指定类型,将按照Object类型操作 MainClass mainClass = new MainClass(100); Object key3 = mainClass.getKey(); System.out.println(key3); //100 //泛型类不支持基本数据类型,只支持包装类类型 //MainClassint> objectMainClass = new MainClassint> (); //同一泛型类,根据不同的数据类型创建的对象,本质是同一类型 System.out.println(stringMainDemo.getClass()); //class itiheima_09.MainClass System.out.println(intMainDemo.getClass()); //class itiheima_09.MainClass System.out.println(stringMainDemo.getClass() == intMainDemo.getClass()); //true } }
总结
泛型类,如果没有指定具体的数据类型,此时,操作类型是Object
泛型的类型参数只能是包装类类型,不能是具体数据类型
泛型类型在逻辑上可以看成是多个不同的类型,实际上都是相同类型
案例
抽奖实现
- ProductGetter
package itiheima_10; import java.util.ArrayList; import java.util.Random; public class ProductGetterT> { Random random = new Random(); //奖品 private T product; //奖品池 ArrayListT> list = new ArrayList> (); //添加奖品 public void addProduct(T t){ list.add(t); } //抽奖 public T getProduct() { product = list.get(random.nextInt(list.size())); return product; } }
- MainClass
package itiheima_10; public class MainClass { public static void main(String[] args) { //创建抽奖类对象,指定数据类型 ProductGetterString> stringProductGetter = new ProductGetter> (); String[] strProducts = { "苹果手机","华为手机","扫地机器人","咖啡机"} ; //遍历数组 for (int i = 0; i strProducts.length; i++) { //添加到奖池 stringProductGetter.addProduct(strProducts[i]); } //抽奖 String product1 = stringProductGetter.getProduct(); System.out.println("恭喜您,你抽中了:" + product1); System.out.println("================================"); ProductGetterInteger> integerProductGetter = new ProductGetter> (); int[] intProducts = { 10000,5000,3000,500,300000} ; for (int i = 0; i intProducts.length; i++) { integerProductGetter.addProduct(intProducts[i]); } Integer product2 = integerProductGetter.getProduct(); System.out.println(product2); } }
equals()和hashCode()方法
hashCode()方法和equals()方法是在Object类中就已经定义了的,所以在java中定义的任何类都会有这两个方法。原始的equals()方法用来比较两个对象的地址值,而原始的hashCode()方法用来返回其所在对象的物理地址
泛型类派生子类
- 子类是泛型类,子类和父类泛型类型要一致
class ChildGenericT> extends GenericT>
- 子类不是泛型类,父类要明确泛型的数据类型
class ChildGeneric extends GenericString>
泛型接口
接口中定义了一个泛型方法 doSomething
,该方法接受一个类型为 T
的参数并返回一个类型为 T
的结果。使用该接口时,可以根据需要指定具体的类型参数
public interface MyInterfaceT> { T doSomething(T t); }
泛型方法
方法使用了一个类型参数 T
,并返回了一个 T
类型的元素。这个方法可以用于任何类型的数组
public static T> T getFirst(T[] array) { if (array.length == 0) { return null; } return array[0]; }
调用泛型方法,你需要在方法名之前指定它的类型参数
String[] words = { "hello", "world"} ; String firstWord = getFirst(words);
调用了 getFirst
方法,并将字符串类型的数组传递给它。因为 getFirst
方法是一个泛型方法,编译器会推断出类型参数 T
是 String
,所以该方法返回了第一个字符串元素 "hello"
类型通配符
类型通配符是Java中的一种特殊语法,用于表示未知类型。它使用问号(?)作为通配符,可以出现在泛型类、泛型方法、变量声明等位置,在泛型类或方法中,类型通配符可以用来表示任何类型
public class BoxT> { public void setValue(T value) { ... } public T getValue() { ... } // 使用类型通配符定义一个方法,可以接受任何类型的Box对象 public void copyValue(Box?> box) { T value = box.getValue(); // 读取box中的值 setValue(value); // 将值设置到当前对象中 } }
类型通配符的上限
类型通配符的上限指定了通配符所代表的类型的最大边界。在 Java 中,可以使用 extends 关键字指定类型通配符的上限。例如,如果要创建一个泛型方法,该方法只能接受 Number 类型及其子类的参数,则可以使用以下语法:
public T extends Number> void methodName(T parameterName) { // 方法实现 }
在这个例子中,泛型通配符 ? super Integer>
的下限是 Integer 类型的父类,这意味着该方法可以接受类型为 Integer、Number、Object 等超类的 List 参数
类型擦除
Java类型擦除是一种编译时的行为,它指在编译Java泛型代码时,将泛型类型信息擦除,转换成对应的原生类型,以保持与旧版Java语言代码的兼容性。具体解释如下:
Java泛型是在JDK1.5引入的,它允许程序员在定义类、接口或方法时使用一个或多个类型参数,用来限定该类、接口或方法中的某些数据类型。例如,我们可以定义一个泛型类ListT> ,其中T表示元素的类型。在实例化该类时,我们需要提供具体的类型参数,例如ListString> 或ListInteger> 。
然而,在编译过程中,Java编译器会将泛型类型信息擦除掉,转换成对应的原生类型。例如,ListString> 被擦除成List,ListInteger> 也被擦除成List。意味着,运行时无法获取泛型类型信息,只能得到原生类型的信息。
这种类型擦除的行为对于Java语言的兼容性非常重要,因为它使得新版本的Java语言可以与旧版本的Java语言保持兼容。但是,它也带来了一些限制和挑战,例如无法使用泛型类型作为静态变量、局部变量、方法参数、异常类型等,还需要通过反射来获取泛型信息
泛型数组
泛型数组是指可以存储任意类型元素的数组,它在声明时需要指定元素类型的占位符,例如:
其中,T
是一个类型参数(type parameter),可以被任意类型所替换(例如 String
、Integer
等)
T[] arr = new T[10]; // 使用占位符 T 声明一个长度为 10 的泛型数组
运行时,Java 虚拟机是无法获知 T
的实际类型的,因此无法创建一个真正的泛型数组。因此,上面的代码会导致编译错误。要想创建一个泛型数组,可以通过类型擦除和强制类型转换来实现,例如:
Object[] arr = new Object[10]; // 创建 Object 类型的数组 T t = (T) arr[0]; // 强制类型转换为 T 类型
这种方式虽然可以创建一个泛型数组,但不建议使用,因为强制类型转换可能会导致运行时错误。通常情况下,可以使用集合类(例如 ArrayList
)来代替泛型数组,以避免这个问题
ArrayListT> list = new ArrayList> (); T t = list.get(0); // 直接获取 T 类型元素
这样就不需要使用强制类型转换来将 Object 类型转换为 T 类型了,同时也可以通过 ArrayList 的动态扩容特性方便地添加或删除元素
泛型反射
泛型是一种编程语言的特性,允许程序员可以在编译时不指定类型,而在运行时再确定类型,从而提高代码的灵活性和重用性。反射是Java中一个非常强大的特性,它使得程序可以在运行时获取类、方法、属性等的信息,并且可以动态地调用这些对象
泛型反射的实现就是对泛型类型进行反射操作。通过泛型反射可以获取泛型类型的参数类型、父类、接口等信息,还可以创建泛型对象、调用泛型方法等。为了实现泛型反射,需要使用到Java中的Type、ParameterizedType、GenericArrayType等相关的API
下面是一个简单的泛型反射示例:
public class GenericT> { private T value; public Generic(T value) { this.value = value; } public T getValue() { return value; } } public class Main { public static void main(String[] args) throws Exception { GenericInteger> generic = new Generic> (123); // 获取泛型参数类型 Type type = generic.getClass().getGenericSuperclass(); if (type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType) type; Type[] argTypes = paramType.getActualTypeArguments(); System.out.println(argTypes[0]); } // 创建泛型对象 Class?> clazz = Class.forName("Generic"); Constructor?> constructor = clazz.getDeclaredConstructor(Object.class); constructor.setAccessible(true); GenericString> generic2 = (GenericString> ) constructor.newInstance("hello"); System.out.println(generic2.getValue()); } }
这个示例中定义了一个泛型类GenericT>
,并在其中获取泛型参数类型、创建泛型对象。运行结果为:
class java.lang.Integer hello
声明:本文内容由网友自发贡献,本站不承担相应法律责任。对本内容有异议或投诉,请联系2913721942#qq.com核实处理,我们将尽快回复您,谢谢合作!
若转载请注明出处: Java泛型
本文地址: https://pptw.com/jishu/290229.html