Java 中的泛型提供了一种创建可以处理不同类型数据的可重用代码的方法。它允许用户定义可操作各种数据类型的类、接口和方法,而无需牺牲类型安全性。在 Java 5 中引入的泛型已经成为 Java 编程语言的一个基本特性。
在 Java 引入泛型之前,它使用原始类型来允许将各种类型的对象存储在集合中。然而,这种做法存在着类型安全性不足的问题,经常导致运行时错误,也让代码变得更加难以理解和维护。泛型的出现解决了这些问题,它通过在编译时进行类型检查和类型推断来确保类型安全性,让代码更加可靠、清晰并且易于维护。
以下是 Java 中与泛型相关的一些关键概念:
? extends Type
) 和下界通配符 ( ? super Type
)。上界通配符允许作为指定类型的子类型的任何类型,而下界通配符允许作为指定类型的超类型的任何类型。泛型带来了许多好处,比如提高了类型安全性、促进了代码重用,并且能让代码更加简洁。它们使您能够编写更通用的算法和数据结构,可以处理多种类型,同时保持了编译时的类型检查。借助泛型,您能够创建更为健壮且易于维护的 Java 代码。
Java 泛型提供了多个优点,有助于编写更安全、更灵活和更可重用的代码。以下是 Java 泛型的一些主要优点:
总的来说,Java 泛型在多个方面都带来了明显的优势,包括类型安全、代码重用、可读性、可维护性以及集合安全性。它们让能够编写健壮且灵活的代码,减少了与类型相关的错误,并且有助于促进更优秀的软件工程实践。
下面是一个示例程序,演示了 Java 中泛型的使用:
/**
* GenericDemo类, 用于演示泛型的使用
* @param <F>
*/
public class GenericDemo<F> {
private F value;
public GenericDemo(F value) {
this.value = value;
}
public F getValue() {
return value;
}
public void setValue(F value) {
this.value = value;
}
public static void main(String[] args) {
GenericDemo<String> stringDemo = new GenericDemo<>("Hello, FunTester!");
System.out.println("String demo: " + stringDemo.getValue());
GenericDemo<Integer> integerDemo = new GenericDemo<>(42);
System.out.println("Integer demo: " + integerDemo.getValue());
}
}
在此示例中,我们有一个名为 的泛型类GenericExample
,它可以与任何类型一起使用T
。它有一个value
类型为 的私有字段T
,以及用于操作该值的构造函数、getter 和 setter 方法。
在该main
方法中,我们创建了两个实例GenericExample
:一个具有类型参数String
,另一个具有类型参数Integer
。我们使用不同的值初始化它们,然后使用该getValue
方法检索并打印这些值。
当我们运行程序时,它会输出:
String demo: Hello, FunTester!
Integer demo: 88
该类GenericExample
是用不同的类型(String
和Integer
)实例化的,并且无论类型如何,代码都保持不变。这演示了泛型如何允许我们编写可用于不同类型的可重用代码。
以下是一些展示 Java 中不同类型泛型的示例:
public static <K, V> void printMap(Map<K, V> map) {
for (Map.Entry<K, V> entry : map.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
}
public static void main(String[] args) {
Map<String, Integer> users = new HashMap<>();
users.put("张三", 27);
users.put("李四", 30);
users.put("王武", 33);
printMap(users);
}
在此示例中,我们有一个通用方法printMap
,可以采用Map
任何键值类型。该方法迭代映射条目并打印它们。在该main
方法中,我们创建一个Map
包含String
键和Integer
值的对象并将其传递给该printMap
方法。
public class NumerGeneric<F extends Number> {
private F value;
public NumerGeneric(F value) {
this.value = value;
}
public double square() {
double num = value.doubleValue();
return num * num;
}
public static void main(String[] args) {
NumerGeneric<Integer> intBox = new NumerGeneric<>(5);
System.out.println("Square of Integer: " + intBox.square());
NumerGeneric<Double> doubleBox = new NumerGeneric<>(3.14);
System.out.println("Square of Double: " + doubleBox.square());
}
}
在此示例中,我们有一个泛型类NumerGeneric
,它仅接受扩展的类型Number
。它有一个初始化值的构造函数和一个square
计算值平方的方法。在该main
方法中,我们创建NumerGeneric
withInteger
和Double
types 的实例,然后调用该square
方法。
/**
* 数组实现的栈
* @param <F>
*/
public class ArrayStack<F> extends Stack<F> {
private List<F> stack = new ArrayList<>();
/**
* push一个元素到栈顶
* @param element the item to be pushed onto this stack.
*/
public void push(F element) {
stack.add(element);
}
/**
* pop栈顶元素
* @return
*/
public F pop() {
if (isEmpty()) {
throw new NoSuchElementException("Stack 为空");
}
return stack.remove(stack.size() - 1);
}
/**
* 返回栈顶元素
* @return
*/
public boolean isEmpty() {
return stack.isEmpty();
}
public static void main(String[] args) {
Stack<String> stringStack = new ArrayStack<>();
stringStack.push("Java");
stringStack.push("is");
stringStack.push("Fun");
while (!stringStack.isEmpty()) {
System.out.println(stringStack.pop());
}
}
}
Stack
在此示例中,我们使用方法push
、pop
和定义通用接口isEmpty
。然后,我们使用一个ArrayStack
使用泛型List
来存储元素的类来实现该接口。在该main
方法中,我们创建一个ArrayStack
withString
类型的实例,并在堆栈上执行压入和弹出操作。
这些示例演示了 Java 中泛型的多功能性,允许您以泛型和类型安全的方式处理不同的类型。
Java泛型中的通配符提供了一种指定未知类型或一系列类型的方法。它们在使用泛型类、接口和方法时提高了灵活性。通配符有两种类型:上限通配符 ( ? extends Type
) 和下限通配符 ( ? super Type
)。
? extends Type
):上限通配符将未知类型限制为特定类型或其任何子类型。它允许您指定参数可以是扩展或实现特定类或接口的任何类型。public static double sumOfList(List<? extends Number> numbers) {
double sum = 0.0;
for (Number number : numbers) {
sum += number.doubleValue();
}
return sum;
}
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(1, 2, 3);
double sum = sumOfList(integers);
System.out.println("Sum: " + sum);
}
在此示例中,该方法sumOfList
接受List
带有上限通配符的a <? extends Number>
。这意味着它可以接受扩展的任何类型的列表Number
,例如Integer
、Double
或Float
。该方法迭代列表并计算数字的总和。
? super Type
):下界通配符将未知类型限制为特定类型或其任何超类型。它允许您指定参数可以是特定类或接口的超类或超接口的任何类型。public static void printElements(List<? super Integer> list) {
for (Object element : list) {
System.out.println(element);
}
}
public static void main(String[] args) {
List<Number> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20L);
numbers.add(30.5);
printElements(numbers);
}
在此示例中,该方法printElements
接受List
带有下限通配符的a <? super Integer>
。这意味着它可以接受任何类型的超类列表Integer
,例如Number
或Object
。该方法迭代列表并打印每个元素。
当您有需要对未知类型或一系列类型进行操作的通用代码时,通配符可以提供灵活性。它们允许您通过容纳不同的类型来编写更通用和可重用的代码,而无需牺牲类型安全性。
?
):Java 泛型中的无界通配符,仅用问号表示?
,通过接受任何类型来实现最大的灵活性。当你想要使用泛型类、接口或方法而不指定任何类型限制时,它非常有用。下面是一个演示无界通配符用法的示例:public static void printList(List<?> list) {
for (Object element : list) {
System.out.println(element);
}
}
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>();
integers.add(1);
integers.add(2);
integers.add(3);
List<String> strings = new ArrayList<>();
strings.add("Hello");
strings.add("World");
printList(integers);
printList(strings);
}
在此示例中,我们有一个名为 的方法printList
,它接受List
带有无界通配符的a List<?>
。这意味着该方法可以接受List
任何类型的 a。
在该main
方法中,我们创建两个List
实例 - 一个具有Integer
类型,另一个具有String
类型。然后我们调用该printList
方法并传入这些列表。该方法迭代列表的元素并打印它们。
通过使用无界通配符,该printList
方法变得通用并且可以处理List
任何类型的实例。这允许最大的灵活性,因为它接受和处理列表而对元素类型没有任何限制。
总体而言,Java泛型为开发者带来了显著的优势,使得代码更加安全、灵活、可维护,并促进了更好的软件工程实践。