什么是java的泛型?
Java泛型是在JDK 5.0中引入的,引入泛型的好处是安全简单。泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
泛型基础
让我们想象一个场景,我们想要在Java中创建一个列表来存储Integer;我们很容易写:
Listlist=newLinkedList();
list.add(newInteger(1));
Integeri=list.iterator().next();
令人惊讶的是,编译器会抱怨最后一行。它不知道返回什么数据类型。编译器将需要显式强制转换:
Integeri=(Integer)list.iterator.next();
没有可以保证列表的返回类型为Integer。此时列表可以容纳任何对象。我们只知道我们通过检查上下文来检索列表。在查看类型时,它只能保证它是一个Object,因此需要进行显式强制转换以确保该类型是安全的。
这种转换可能很烦人,我们知道此列表中的数据类型是Integer。演员表也使我们的代码混乱。如果程序员在显式转换中犯了错误,则可能导致与类型相关的运行时错误。
如果程序员可以表达使用特定类型的意图,而编译器可以确保这种类型的正确性,则容易得多。这是泛型背后的核心思想。
让我们将前面的代码片段的第一行修改为:
List<Integer>list=newLinkedList<>();
通过添加包含该类型的菱形运算符<>,我们仅将此列表的特殊化范围缩小为Integer类型,即,我们指定将在列表内保留的类型。编译器可以在编译时强制执行类型。在小型程序中,这看似微不足道,但是,在大型程序中,这可以增加强大的鲁棒性,并使程序更易于阅读。
泛型方法
泛型方法是用单个方法声明编写的那些方法,并且可以使用不同类型的参数来调用。编译器将确保使用任何一种类型的正确性。这些是泛型方法的一些属性:
- 泛型方法在方法声明的返回类型之前具有类型参数(包含该类型的菱形运算符)
- 类型参数可以是有界的(边界将在本文后面进行解释)
- 泛型方法可以在方法签名中使用逗号分隔不同类型的参数
- 通用方法的方法主体就像普通方法一样
定义将数组转换为列表的通用方法的示例:
public<T>List<T>fromArrayToList(T[]a){
returnArrays.stream(a).collect(Collectors.toList());
}
在前面的例子中,在方法签名意味着该方法将被处理的通用型Ť。即使该方法返回void,这也是必需的。
如上所述,该方法可以处理一个以上的泛型类型,在这种情况下,必须将所有泛型类型添加到方法签名中,例如,如果我们要修改上述方法以处理类型T和类型G,应该这样写:
publicstatic<T,G>List<G>fromArrayToList(T[]a,Function<T,G>mapperFunction){
returnArrays.stream(a)
.map(mapperFunction)
.collect(Collectors.toList());
}
我们传递了一个函数,该函数将具有T类型的元素的数组转换为具有G类型的元素的列表。一个示例是将Integer转换为其String表示形式:
@Test
publicvoidgivenArrayOfIntegers_thanListOfStringReturnedOK(){
Integer[]intArray={1,2,3,4,5};
List<String>stringList
=Generics.fromArrayToList(intArray,Object::toString);
assertThat(stringList,hasItems(\"1\",\"2\",\"3\",\"4\",\"5\"));
}
泛型边界
-
上边界
当泛型T给定形如 的A类型到A类型任何子类的限制域,可以匹配任何在此限制域中的类型,此种表示叫上边界通配符
上边界
上边界不能往里存,只能往外取,请看下面例子:
List<?extendsList>lists2=newArrayList<>();
//编译器正确
Listlist1=lists2.get(0);
//编译器报错
lists2.add(newLinkedList());
-
下边界
当泛型T给定形如 的A类型到A类型任何父类的限制域,可以匹配任何在此限制域中的类型,此种表示叫下边界通配符。
下边界
上边界只能往里存,不能往外取,请看下面例子:
List<?superList>lists=newArrayList<>();
//编译器正确
lists.add(newArrayList());
//编译器正确
lists.add(newLinkedList());
//编译器报错
Listlist=lists.get(0);
通配符 <? extends A>
通配符由Java中的问号表示。”,它们用于指代未知类型。通配符在使用泛型时特别有用。
publicstaticvoidpaintAllBuildings(List<?extendsBuilding>buildings){
...
}
泛型擦除
泛型已添加到Java中,以确保类型安全并确保泛型不会在运行时引起开销,编译器在编译时对泛型应用了一个称为类型擦除的过程。
类型擦除会删除所有类型参数,并用其边界或对象(如果类型参数是无界的)替换它。因此,编译后的字节码仅包含常规类,接口和方法,从而确保不会产生任何新类型。在编译时,正确的转换也适用于Object类型。
下面是类型擦除的一个示例:
public<T>List<T>genericMethod(List<T>list){
returnlist.stream().collect(Collectors.toList());
}
使用类型擦除时,无界类型T被Object替换,如下所示:
//擦除后的方法只剩下Object
publicList<Object>withErasure(List<Object>list){
returnlist.stream().collect(Collectors.toList());
}
//whichinpracticeresultsin
publicListwithErasure(Listlist){
returnlist.stream().collect(Collectors.toList());
}
如果类型是有界的,那么在编译时,该类型将被有界的代替:
public<TextendsBuilding>voidgenericMethod(Tt){
...
}
编译后会改变:
publicvoidgenericMethod(Buildingt){
...
}
END
泛型是Java语言的强大功能,它使我们的开发工作更加简单,并且不易出错。泛型在编译时强制类型正确,最重要的是,使用泛型是不会给我们的应用程序造成任何额外的开销。
End
欢迎关注公众号!公众号回复:
入群
,扫码加入我们交流群!