阅读本章内容,你可以学到如下知识:
泛型就是具体的类型参数化,在具体传入或者调用时才去传入具体的类型。(指定容器要持有什么类型的对象,由编译器保证类型的正确性)
先看个例子,以我们最常用的list集合举例,如果我们没有泛型,集合里面放的都是Object对象
- public static void main(String[] args) {
- List
- listObject.add(1);
- listObject.add(new Album());
- System.out.println(listObject);
- for (Object object : listObject) {
- String str = (String) object;
- System.out.println(str);
- }
-
- List
listStr = new ArrayList<>(); - listStr.add("str1");
- listStr.add("str12");
- for (String s : listStr) {
- System.out.println(s);
- }
- }
从上面代码我们可以发现两个问题
listObject其实放的是Integer和Number类型,我们在编译时可以随意强制转换然后进行方法调用,但是我们将Integer和Number类型都转换为String其实在运行期是会报错的,我们这里的错误就推迟到运行期才暴露出来,而使用泛型我们在编译器就可以提前做类型检查,这样错误就在编译期暴露出来了listStr集合,因为我们指定了泛型是String,所以当我们使用list中的元素就不用做强制转换。我们再看一段代码
- public class Main {
-
- @Test
- public void test() {
- String messageContent = "[message]:泛型真好用";
- String commentContent = "[comment]:泛型真好用";
- Message message = new Message();
- Comment comment = new Comment();
- execute(messageContent, message::setContent);
- execute(commentContent, comment::setContent);
- System.out.println(message.getContent()); // [message]:泛型真好用
- System.out.println(comment.getContent()); // [comment]:泛型真好用
- }
-
- private
void execute(T t, Consumer consumer) { - consumer.accept(t);
- }
-
- }
-
- class Message {
- private String content;
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
- }
-
- class Comment {
- private String content;
-
- public String getContent() {
- return content;
- }
-
- public void setContent(String content) {
- this.content = content;
- }
- }
Message和Comment,然后我们定义了一个execute方法,传入了一个泛型和一个消费型函数,我们在里面执行accept方法,这样我们这个方法相当于是一个通用的算法,我们可以传入任意对象然后实现不同的consumer函数就行所以可以总结一下我们为什么要使用泛型
在编译时会有更强的类型检查,使我们在编译期就可以发现一些类型转换的问题(修复编译期错误比修复运行期错误更容易,因为运行期的错误很难找到)
我们使用泛型,在重写时定义好了泛型类型,就不需要在代码中进行强制转换了
我们可以通过泛型实现更加通用的算法
E:元素(在java集合框架中广泛使用)K:键(MapN:数字T:类型V:值 (作为返回值或者映射值)S,U,V等:第2,第3,第4类型它们其实是可以完全互换的,只是我们这样定义是一个约定
老规矩,我们还是先看代码
- class Node
{ -
- private K k;
- private V v;
-
- public Node(K k, V v) {
- this.k = k;
- this.v = v;
- }
-
- public K getK() {
- return k;
- }
-
- public void setK(K k) {
- this.k = k;
- }
-
- public V getV() {
- return v;
- }
-
- public void setV(V v) {
- this.v = v;
- }
-
- public
extends BaseInteface> void sengMessage(T t){ - System.out.println(t.num);
- }
-
-
- }
类或者接口上面使用泛型我们需要在类名后面写一个<>,然后再写上我们的泛型(定义几个泛型,<>放几个泛型),我们在方法上面就可以使用这些泛型了
我们在方法上使用泛型有两种方式
我们接下来再看看我们经常说的上界通配符和下界通配符
- public class Wildcar
extends Number, V extends BaseIntefaceImpl>{ -
- private T t;
- private V v;
-
- public Wildcar(T t, V v) {
- this.t = t;
- this.v = v;
- }
-
- public void show(List super Number> lists){
-
- }
-
- public static void main(String[] args) {
- Wildcar
intefaceWildcar = new Wildcar<>(1, new BaseInfoParent()); - System.out.println(intefaceWildcar.t.intValue());
- System.out.println(intefaceWildcar.v);
- }
- }
T extends Number:这里是使用的上界通配符,我们在后面代码中所定义的T只能是extend的子类
List super Number> lists:这里使用的是下界通配符,表示填充为任意Number的父类