Java泛型
Java 泛型是一种在编写 Java 代码时用来提高类型安全性和重用性的机制。Java 泛型允许我们定义和使用一些通用的类、接口和方法,这些通用的类、接口和方法可以用于操作不同类型的数据。
下面是常见的Java 泛型的几种表现形式:
public class List<T> { ... }
public static <T> T[] toArray(T[] a) { ... }
public interface Map<K,V> { ... }
在上面的例子中,T、K、V 是类型参数,它们代表泛型类、泛型方法和泛型接口中的数据类型。使用泛型类、泛型方法和泛型接口时,我们可以传递实际的类型参数,例如:
List<String> list = new ArrayList<String>();
String[] array = toArray(list.toArray(new String[list.size()]));
Map<String,Integer> map = new HashMap<String,Integer>();
在这些例子中,我们可以看到使用泛型时传递了实际的类型参数。使用 Java 泛型可以使我们编写更加灵活的代码,提高代码的重用性和类型安全性,同时也可以提高程序的性能。
为什么引入泛型 - 泛型的目的和优势
泛型是在 Java 5中引入,Java 泛型的目的是让编程变得更加类型安全,减少代码中类型转换的错误,并增加代码的重用性。通过使用泛型,我们可以编写更加灵活的代码,而不必依赖于特定的数据类型。例如,如果我们要编写一个通用的容器类,可以使用泛型类,这样就可以避免使用 Object 类型,而不必担心类型转换错误的问题。
先看一个简单的应用:
public class Stack<T> {
private T[] elements;
private int size = 0;
public Stack(int capacity) {
elements = (T[]) new Object[capacity];
}
public void push(T element) {
elements[size++] = element;
}
public T pop() {
if (size == 0) {
throw new EmptyStackException();
}
T element = elements[--size];
elements[size] = null;
return element;
}
public boolean isEmpty() {
return size == 0;
}
}
Stack<Integer> intStack = new Stack<Integer>(10);
intStack.push(1);
intStack.push(2);
intStack.push(3);
System.out.println(intStack.pop()); // output: 3
如果在引入泛型以前上面的代码需要使用Object类,来看看它的实现:
public class Stack {
private Object[] elements;
private int size = 0;
public Stack(int capacity) {
elements = new Object[capacity];
}
public void push(Object element) {
elements[size++] = element;
}
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object element = elements[--size];
elements[size] = null;
return element;
}
public boolean isEmpty() {
return size == 0;
}
}
Stack stack = new Stack(10);
stack.push(1);
stack.push(2);
stack.push(3);
int i = (Integer) stack.pop();
System.out.println(i); // output: 3
通过比较这两种实现方式,我们可以看到使用泛型可以使代码更加简洁和类型安全。在使用泛型的代码中,我们可以直接使用具体的数据类型,而不必进行类型转换,可以避免一些类型转换错误和运行时异常的问题。
再看看下一个例子:
List list = new ArrayList();
list.add("hello");
list.add(123);
String str = (String) list.get(1); // 运行时出错,因为 123 无法强制转换为字符串类型
//又或者你在使用list里面的数据前先做一个类型判断再使用
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); 不能添加整数类型,因为 list 的类型参数为 String
String str = list.get(0); // 可以直接获取字符串类型,无需强制转换
前后对比显然第二段代码更简洁、明了和安全。
Java 泛型的优势包括:
类型安全性:Java 泛型可以让我们在编写代码时明确操作的数据类型,从而可以在编译时进行类型检查,避免类型转换错误和运行时异常。
代码重用性:使用泛型可以编写通用的类、接口和方法,可以在不同的应用程序中重复使用,提高代码的重用性。
程序可读性:泛型可以使代码更加清晰易读,因为我们可以在代码中直接使用具体的数据类型,而不是使用 Object 类型。
性能优化:泛型可以减少自动装箱和拆箱的操作,提高程序的性能。
Java 泛型的基本使用
先了解一个概念:类型参数。类型参数(type parameter)是指在泛型定义中所使用的占位符类型,它是一个未知的类型,用于代定某个具体类型。
Java 泛型不仅可以应用于类,还可以应用于方法和接口,分别称为泛型类、泛型方法和泛型接口。
泛型类
泛型类是具有一个或多个类型参数的类。在类定义中,类型参数使用尖括号(<>)括起来,可以用来表示类中的属性、方法的参数或返回值等。例如,下面的代码中的 T 和 E 就是类型参数:
public class Pair<T, E> {
private T first;
private E second;
public Pair(T first, E second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return first;
}
public void setFirst(T first) {
this.first = first;
}
public E getSecond() {
return second;
}
public void setSecond(E second) {
this.second = second;
}
}
在上面的代码中,Pair 类定义了两个类型参数 T 和 E,用于表示第一个和第二个元素的类型。这样,我们就可以创建不同类型的 Pair 对象,例如 Pair<String, Integer>、Pair<Integer, Double> 等。
泛型方法
泛型方法是在方法定义中使用类型参数的方法。在方法定义中,类型参数出现在方法名和参数列表之间的尖括号(<>)中,用于表示方法的参数类型、返回值类型或方法内部的局部变量类型。例如:
public class Utils {
public static <T> void printArray(T[] array) {
for (T element : array) {
System.out.println(element);
}
}
}
在上面的代码中,我们定义了一个名为 printArray 的泛型方法,它接受一个类型参数为 T 的数组作为参数,用于打印数组中的所有元素。在方法定义中,我们使用了类型参数 T 来表示数组元素的类型,可以根据需要传入不同类型的数组。
泛型接口
泛型接口是具有一个或多个类型参数的接口。在接口定义中,类型参数使用尖括号(<>)括起来,可以用来表示接口中的方法的参数或返回值等。例如:
public interface List<T> {
void add(T element);
T get(int index);
}
在上面的代码中,我们定义了一个名为 List 的泛型接口,具有一个类型参数 T,用于表示列表中的元素类型。在 List 接口中,我们定义了两个方法 add 和 get,它们的参数和返回值都使用了类型参数。这样,我们就可以创建不同类型的列表实现,例如 ArrayList<String>、LinkedList<Integer> 等,它们都是 List 接口的实现类,但元素类型不同。
本文只是个人(EnergyNo8)学习笔记如有错误请网友指出。
如有引用或转载请标明出处!
努力、坚持,总会有收获!
______ EnergyNo8