1. 介绍
泛型(Generics):是Java语言中一项重要特性,允许在类、接口和方法中使用参数化类型。通过泛型,可以编程通用、灵活的代码。提高代码的重用性和类型安全性。
泛型是Java SE 1.5的新特性,是一种不确定的数据类型,这种不确定的数据类型需要我们在使用这个类的时候它才能够确定出来,早期的Object类型可以接收任意的对象类型,但是在实际的使用过程当中,会出现类型转换的问题,会报出一个异常,使用泛型就可以避免这种问题,因为泛型可以使编译器在编译期间对类型进行检查以次来提高类型安全,减少运行时由于对象类型不匹配引发的异常。
具体来说,泛型允许我们在编写类、接口和方法时使用类型参数,而不是具体的数据类型。这些类型参数在使用时,可被指定为具体的数据类型,从而使得代码可以适用于不同类型的数据,而不必为每种类型编写单独的代码。如果不确定使用什么类型的数据就使用泛型。从概念而言就是一个预定义数据类型。
优点:
1) 类型安全性和程序健壮性:类型安全性和程序健壮性是相互关联的。泛型在编译时对类型进行检查,提高了程序的健壮性,避免在运行时由于类型转换错误而导致的异常。入:ClassCastException异常。
ClassCastException异常是JVM在检测到两个类型转换不兼容时引发的运行时异常。
2) 代码重用性和简洁性:代码重用性和简洁性是相互关联的。通过泛型可以编译通用的,可复用的代码来提高代码的重用性。同时泛型也使得代码更加简洁、易读;因为它消除了很多显式的类型转换和重复的代码。
3) 减少类型转换:泛型的主要优点是减少了代码中的类型转换。
2. 泛型集合和非泛型集合
泛型集合:是指能够存储特定类型对象的集合,其中的元素类型在集合被创建时是确定,并且集合只能存储该特定类型的元素。泛型集合通过泛型机制提供了类型安全的存储和检索操作。
默认为Object类型,接受的类型可以是引用数据类型、Boolean类型。如果指定泛型类型为String类型,存储的类型只能是String类型,可以存储多个元素。
泛型必须是包装类,只能代表引用数据类型。在程序总有些数据会返回空值,用剧本数据类型int会发生异,因为int内有null值;但基本数据类型对应的Integer包装类型不会,因为对象可以为NULL。
// 泛型集合 // 需要一个存储字符串的列表,并且希望这个列表能够保证类型安全,那么我们可以使用泛型集合 ArrayList。 public static void main(String[] args) { // 创建一个存储字符串的泛型集合 ArrayList<String> stringList = new ArrayList<>(); // 向集合内添加元素 stringList.add("Hello,"); stringList.add("Generic."); // 遍历打印 System.out.println("字符串集合内的元素:"); for (String list : stringList) { System.out.println(list); } // 从集合内获取元素 String accessElement = stringList.get(0); System.out.println("第一个元素为:" + accessElement); }
// 泛型集合 // 创建一个泛型方法,该方法接受一个泛型列表作为参数,并打印出列表中的所有元素。这样的方法可以接受任何类型的列表,并且具有通用性。 public static void main(String[] args) { ArrayList<Integer> integerList = new ArrayList<>(); integerList.add(2024); ArrayList<String> stringList = new ArrayList<>(); stringList.add("Hello,2024!"); System.out.println("整数列表:"); printlnList(integerList); System.out.println("字符串列表:"); printlnList(stringList); } // 泛型方法,接受一个泛型列表并打印其中所有元素 public static <T> void printlnList(ArrayList<T> arrayList) { for (T element : arrayList) { System.out.println(element); } }
非泛型集合:
// 非泛型集合 public static void main(String[] args) { // 非泛型集合,存储元素用Object类型,add接收的类型可以是基本数据类型,引用数据类型,布尔类型 ArrayList<Object> nonGenericCollectionList = new ArrayList<>(); // 列表内添加不同类型的对象 nonGenericCollectionList.add("学Java的Bei"); // 字符串 nonGenericCollectionList.add(20240225); // 整数 nonGenericCollectionList.add(true); // 布尔 // 列表内获取元素,并进行元素转换 String string = (String) nonGenericCollectionList.get(0); int num = (int)nonGenericCollectionList.get(1); Boolean bool = (boolean)nonGenericCollectionList.get(2); // 打印元素 System.out.println("string = " + string); System.out.println("num = " + num); System.out.println("bool = " + bool); // string = 学Java的Bei // num = 20240225 // bool = true }
注意:
-
- 在创建集合时指定集合存储的元素类型;例:ArrayList<Integer>表示存储整数类型的元素。
- 遍历集合时不需行类型转换;因为集合内的元素类型已经确定。
- 泛型集合在编写通用代码和提高代码安全性方面非常有用。
使用非泛型集合时,需明确存储每个元素的数据类型,否则会引发 异常 ClassCastException。
3. 泛型类
泛型类是使用泛型类型参数的类。允许类中某些字段、方法或构造函数接受特定类型的数据,而这些类型在类被实例化时才确定。
泛型标识(也称为类型参数):在Java中指定泛型类,接口或方法的参数类型。允许在编写代码时使用占位符来表示数据类型,而不需提前确定具体的参数类型;当使用时,再用确定的数据类型替换我们的标识。
在定义泛型类、接口或方法时,使用尖括号<T>、<E>或其他标识符来声明参数类型;这些标识符可以是任何合法的Java标识符,通常用单个大写字母表示,以表它们是参数类型。
- <T>:通用泛型类型,通常表示任意类型;
- <E>:集合元素泛型类型,通常表示集合中的元素类型;
- <K,V>:映射键,值:表示键值对中键和值的参数类型;
// 泛型标识 public class GenericIdentifier<T> { // <T> 是一个泛型标识,它表示任意类型。 // 在实例化 Box 类时,指定了具体的数据类型(整数和字符串),这样就替换了泛型标识,使得 Box 类可以存储不同类型的数据。 // 下面的T仅仅表示的是一种参数类型,这个参数类型是一个变量,可以指代任意一种引用数据类型。 // T可以换成 A-Z 之间的任何一个字母都可以,并不会影响程序的正常运行,但是如果换成其他的字母代替,在可读性上可能会弱一些。 private T date; public GenericIdentifier(T date) { this.date = date; } public T getDate() { return date; } public void setDate(T date) { this.date = date; } } class Main { public static void main(String[] args) { // 创建一个存储整数的GenericIdentifier实例 GenericIdentifier<Integer> integerGenericIdentifier = new GenericIdentifier<>(10); // 创建一个存储字符串GenericIdentifier实例 GenericIdentifier<String> stringGenericIdentifier = new GenericIdentifier<>("学Java的Bei"); // 获取并打印数据 System.out.println("整数:" + integerGenericIdentifier.getDate()); // 整数:10 System.out.println("字符串:" + stringGenericIdentifier.getDate()); // 字符串:学Java的Bei } }
4. 从泛型类派生子类
在Java可以从泛型类派生子类,但要注意一些限制和约束。
1) 如果子类也是泛型类,并且使用父类的参数类型,那么子类的参数类型标识应与父类的参数类型标识一致。否则,在子类中无法得到具体的数据类型,编译器会报错。
// 从泛型类派生子类 public class NoteOne<T> { // SubClass<T> 是 NoteOne<T> 的子类,并且也是一个泛型类。 // 子类 SubClass 继承了父类 NoteOne 的类型参数 T,使得子类可以使用相同的类型参数。 // 这样,子类可以保留父类的泛型类型,并且可以使用相同的类型参数来实例化对象。 private T data; public NoteOne(T data) { this.data = data; } public T getData() { return data; } } // 子类也是泛型类,且继承父类的参数类型 class SubClass<T> extends NoteOne<T>{ public SubClass(T date) { super(date); // super(data) 表示调用父类的构造方法,并将参数 data 传递给父类的构造方法进行处理。 } } class Main { public static void main(String[] args) { // 创建一个存储整数的SubClass实例 SubClass<Integer> integerSubClass = new SubClass<>(2024); System.out.println("整数:" + integerSubClass.getData()); // 创建一个存储字符串的SubClass实例 SubClass<String> stringSubClass = new SubClass<>("学java的Bei"); System.out.println("字符串:" + integerSubClass.getData()); } }2) 如果子类不是泛型类,而父类是泛型类;那么在子类中实例化父类时,需明确指定泛型类的数据类型。这种行情况下,子类不具有泛型类型参数,而是使用具体的数据类型来实例化父类。
public class NoteTwo<T> { // 子类 SubBox 继承了父类 NoteTwo<T>,但子类自身不是泛型类。 // 在子类的构造方法中,通过 super(data) 指定了父类 NoteTwo 的泛型数据类型为 Integer。 // 这样,子类 SubBox 就可以正确地实例化父类 NoteTwo<Integer>。 private T data; public NoteTwo(T data) { this.data = data; } public T getData() { return data; } } // 子类不是泛类型 class SubClassTwo extends NoteTwo<Integer> { public SubClassTwo(Integer date) { super(date); // 指定父类的泛型数据类型为Integer } } class MainTwo{ public static void main(String[] args) { // 创建子类实例,需明确指定父类的泛型数据类型为Integer SubClassTwo subClassTwo = new SubClassTwo(20240225); // 调用父类方法 System.out.println("参数:" + subClassTwo.getData()); // 参数:20240225 } }
标签:子类,数据类型,类型,泛型,集合,public From: https://www.cnblogs.com/warmNest-llb/p/18032723