文完本文转载自微信公众号“爱写bug的米罗”,作者米罗。转载本文请联系爱写bug的Milo公众号。概述大家好,我是Milo,今天我来回顾一下泛型。JDK5.0引入了Java泛型,允许设计者详细描述变量和方法的类型如何变化,使代码更具可读性。本文简要介绍了Java中的泛型,涵盖了泛型背后的目标以及如何使用它们来提高代码质量。为什么要引入泛型?在没有泛型的背景下,让我们想象一个场景,我们想在Java中创建一个List来存储Integer。代码如下:Listlist=newLinkedList();list.add(newInteger(1));Integeri=list.iterator().next();果然IDEA会直接提醒你需要投。我们修改代码如下:Integeri=(Integer)list.iterator.next();没有泛型,定义的List可以保存任何对象。我们遍历的时候,根据上下文判断,只能保证是Object,所以需要显示转??换。我们知道List中的数据类型是Integer,可以直接转换。如果我们不知道或者在转换的时候写错了类型,就会报错,这样就会发生灾难。这时候有人就想,我能不能在使用List的时候指定保存的类型,在编译阶段帮我保证类型的正确性,这样就可以完全避免烦人的强制转换,于是泛型就出现了。让我们修改前面代码片段的第一行:Listlist=newLinkedList<>();通过添加包含类型的菱形运算符<>,我们将List可以容纳的类型限制为仅Integer类型,并且编译器可以在编译时强制执行类型。泛型方法对于泛型方法,我们可以使用不同类型的参数来调用它们。编译器将确保我们使用的任何类型的正确性。通用方法属性:通用方法在方法声明的返回类型之前有一个类型参数(带类型的菱形运算符)。类型参数可以是有界的(我们将在本文后面解释边界)。泛型方法可以有不同类型的参数,在方法签名中用逗号分隔。泛型方法的方法体就像普通方法一样。下面是定义将数组转换为列表的通用方法的示例:publicListfromArrayToList(T[]a){returnArrays.stream(a).collect(Collectors.toList());}方法签名表示该方法将处理泛型类型T。即使该方法返回void,这也是必需的。如前所述,此方法可以处理多个泛型类型。在这种情况下,我们必须将所有泛型类型添加到方法签名中。下面是我们如何修改上面的方法来处理类型T和G:mapperFunction).collect(Collectors.toList());}我们正在传递一个函数,该函数将具有类型T元素的数组转换为具有类型G元素的列表。一个示例是将Integer转换为其String表示形式:@TestpublicvoidgivenArrayOfIntegers_thanListOfStringReturnedOK(){Integer[]intArray={1,2,3,4,5};ListstringList=Generics.fromArrayToList(intArray,Object::toString);assertThat(stringList,hasItems("1","2""3","4","5"));}请注意,Oracle建议对通用类型使用大写字母,并选择更具描述性的性别字母来表示正式类型。在Java集合中,我们使用T表示类型,K表示键,V表示值。有界泛型类型参数可以有界,我们可以限制方法接受的类型。例如,我们可以指定一个方法接受一个类型及其所有子类(上限)或一个类型及其所有超类(下限)。要声明上限类型,我们在类型后使用关键字extends并声明下限类型,我们在类型后使用关键字super。示例:publicListfromArrayToList(T[]a){...}我们在这里使用关键字extends来指示类型T在类的情况下扩展上限或在情况下实现上限的一个接口。多重边界一个类型也可以有多个上限:如果T扩展的类型之一是一个类(例如Number),我们必须将它放在边界列表的第一位。不这样做将导致编译时错误。在泛型中使用通配符在Java中,通配符用?我们用它们来指代未知类型。通配符对泛型特别有用,可以用作参数类型。首先,我们知道Object是所有Java类的超类。但是,Object的集合不是任何集合的超类型。因此,List不是List的超类型,两者没有直接关系。示例:publicstaticvoidpaintAllBuildings(Listbuildings){buildings.forEach(Building::paint);}如果有一个名为House的Building子类型,我们不能对House列表使用此方法,即使House属于Building一个亚型。如果我们需要对类型Building及其所有子类型使用此方法,则可以使用有界通配符:publicstaticvoidpaintAllBuildings(Listbuildings){...}现在此方法将适用于类型Building及其所有子类型。这称为上限通配符,其中类型Building是上限。我们还可以指定具有下限的通配符,其中未知类型必须是指定类型的超类型。可以使用super关键字后跟特定类型来指定下限。例如,表示未知类型,它是T的超类(=T及其所有超类)。类型擦除泛型被添加到Java中以确保类型安全。为了确保泛型不会在运行时产生开销,编译器在编译时对泛型应用称为类型擦除的过程。类型擦除删除所有类型参数,并用它们的边界替换它们,如果类型参数是无界的,则用Object替换它们。这样,编译后的字节码只包含普通的类、接口和方法,确保不会创建新类型。正确的转换也适用于编译时的Object类型。这是类型擦除的示例:publicListgenericMethod(Listlist){returnlist.stream().collect(Collectors.toList());}使用类型擦除,无界类型T是替换为对象:publicList