泛型是Java中的关键概念。大多数Java代码库都将使用它们。因此,不可避免的是某个时候开发人员会遇到它们。这就是为什么正确理解它们至关重要。正确理解泛型也将帮助您获得Java面试机会。
在本文中,我将讨论泛型是什么,如何在Java中使用它们以及它们的优点。
Java 5中添加了泛型,以提供编译时类型检查,并消除了使用集合类时常见的ClassCastException风险。
Java中的集合类用于存储和操作对象组。例如,ArrayList集合类可以存储任何类型的对象。因为它被设计为Java基类类型Object的容器。因此,ArrayList对象可以容纳String或Integer或任何Java类型。使用泛型,我们可以定义ArrayList可以容纳的对象类型。因此允许创建单个对象类型ArrayLists。
public class GenEx1{ |
如您在上面的示例中看到的那样,泛型类型是通过使用尖括号来定义的。在此示例中,只能将String对象存储在ArrayList中。Java中的集合类现在具有通用类型。现在让我们看看如何编写我们自己的通用类,接口和方法。
泛型类
在泛型类声明中,类的名称后跟类型参数部分。我们可以遵循相同的语法来创建泛型接口。类型参数也被称为类型变量,是用于指定一个泛型的类型名称的标识符。泛型类的类型参数部分可以包含一个或多个用逗号分隔的类型参数。这些类也称为参数化类。
class Test<K, V>{ |
在上面的示例中,Test类具有两个名为K和V的类型参数。因此,Test类的对象可以存储两种不同类型的值。
泛型方法
如果我们可以编写一个单一的排序方法来对Integer数组,String数组或任何支持排序的类型的数组中的元素进行排序,那会很好,对吗?Java泛型方法允许我们使用单个方法声明来指定一组相关类型。您将能够编写一个可以用不同类型的参数调用的通用方法声明。必须在方法返回类型之前指定类型参数部分。类型参数也可以用作方法的返回类型。
public class GenEx3{ |
在上面的示例中,printArray方法可用于打印任何类型的数组的元素。
泛型中的有界类型参数
到目前为止,我们只看到了无界限的泛型类型参数。无界意味着我们的泛型类型参数可以是我们想要的任何类型。有时您可能希望限制允许传递给参数的类型。例如,对数字进行操作的方法可能只希望接受Number类或其子类的实例。有界类型参数用于此目的。要声明一个有界的类型参数,请列出该类型参数的名称,然后是extends关键字,然后是其上限。
public abstract class Cage<T extends Animal> { |
在示例中,笼型的通用类型必须始终是Animal或Animal类的子类。因此,我们可以将Cat,Dog或Animal类作为通用类型参数传递。
如果需要,我们还可以为泛型类型声明多个范围。因此,可以修改以下示例中的抽象类,如下所示。
public abstract class Cage<T extends Animal & Comparable<T>> |
在这里,类型参数现在必须同时考虑Animal类和Comparable接口。
泛型中的通配符和子类型
问号(?)是泛型中的通配符,表示未知类型。如果我们希望我们的通用方法适用于所有类型,在这种情况下,可以使用无界通配符。无界通配符由<?>表示。我们还可以使用有界通配符,有界通配符有两种类型:上界通配符和下界通配符。
上界通配符用于在方法中放宽对变量类型的限制。例如,假设我们不知道列表将是数字,整数还是双精度类型。那么我们如何获得该列表中元素的总和?我们可以使用上限通配符来解决此问题。下面的示例显示了如何实现它。
public void method( List<? extends Number> list){ |
下界通配符用于增加方法中对变量类型的限制。假设我们只想将Integers添加到列表中,而我们也想接受Integer的超类型列表。我们可以使用下界通配符来实现此目的。从下面的示例中,我们可以看到如何使用它。
public void addIntegers(List<? super Integer> list){ |
虽然Integer在Java中是一个亚型Number,List<Integer>是不是一个亚型List<Number>?他们的共同父母是List<?>。因此,泛型类中的子类型使用通配符完成。下面的示例将帮助您理解这一点。
ArrayList<? extends Integer> intList = new ArrayList<>(); |
泛型的优势
既然您知道如何使用泛型,那么您必须考虑为什么我们需要使用它们。嗯,我们使用它们的三个主要原因是:
- 类型安全
- 不需要类型转换
- 可重用代码
泛型确保了编译时的安全性,这使您可以在编译代码时捕获无效类型。因此,您无需担心运行时异常。不需要泛型转换是使用泛型的另一个优点。您定义初始类型,然后让代码为您进行转换。我们还可以避免代码重复。没有泛型,我们必须复制并粘贴相同的代码,但要使用不同的类型。使用泛型,我们不必这样做。