Java泛型1——概述

注:以下内容基于Java 8,所有代码都已在Java 8环境下测试通过

目录:

  • Java泛型1——概述
  • Java泛型2——泛型类
  • Java泛型3——泛型接口
  • Java泛型4——泛型方法
  • Java泛型5——泛型通配符
  • Java泛型6——类型擦除

1. 泛型是什么

泛型,即类型参数,类似于函数中的参数,泛型可以将类型参数化。这句话过于抽象,为了便于解释,下面根据个人理解将泛型与函数中的参数做个对比。

从简单的人手,首先说一下函数参数经历的两个阶段:

  • 函数定义

    在定义函数时,只知道参数的类型而不知道具体的值(此时的参数叫做形参)。此时,为了定义函数在参数上的操作,我们需要给参数取个名字,比如下面这个函数:

    private void testF(int a, int b) {
        System.out.println(a);
        System.out.println(b);
    }
    

    在这个函数定义中,只知道有两个 int 类型的参数且名字分别叫a、b,但具体的值此时尚不知道。

  • 函数调用

    在调用函数时,我们需要按照函数的要求传入两个int类型的变量:

    //调用函数
    testF(1. 2);
    

从上面可以看出来,对于函数参数而言:函数在定义时知道参数的类型和名字,但不知道具体的值,在函数调用时才知道具体的值是多少。而泛型更进一步,以泛型方法为例,在定义一个泛型方法时,仅仅知道参数的名字,而参数的类型和具体的值只有在调用函数的时候才能确定!

另外,与函数参数不同的是,类、接口、函数中都可以加入泛型机制,即为类、接口、函数指定类型参数,分别被叫做泛型类、泛型接口、泛型函数

2. 泛型的作用

根据《On Java 8》的描述:“泛型的主要目的之一就是用来约定集合要存储什么类型的对象,并且通过编译器确保规约得以满足”。

Talk is cheap,let me show you the code,假设我们现在有个很简单的需求:需要用一个集合存放String类型的变量,并将这个集合中的内容打印输出。使用泛型可以这样写:

2.1 通过泛型实现

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        LinkedList<String> stringList = new LinkedList<>(); // 是一个泛型
        
        stringList.add("abc");
        stringList.add("def");
        //stringList.add(1); //当添加一个非 String 类型的变量时,编译器会报错
        
        for (int i = 0; i < stringList.size(); i++) {
            System.out.println(stringList.get(i));
        }
    }
}

在上面的代码中:

  1. 是一个泛型,它限制了stringList中只能存放String类型的变量(约定集合要存储什么类型的对象)
  2. stringList.add(1);,由于泛型的存在,当stringList试图添加一个非String类型的变量时,编译器会报错(通过编译器确保规约(也就是只能存放String类型变量)得以满足)

第一点实际展示了如何使用泛型,这再后面还会再详细介绍,先具体说一下第二点:如何理解通过编译器确保规约得以满足?在Java中,Object类是所有类的超类,通过向下转型,下面这段代码可以实现和上面类似的功能:

2.2 通过Object和转型实现

import java.util.LinkedList;

public class Main {
    public static void main(String[] args) {
        LinkedList stringList = new LinkedList(); //未使用泛型机制,此时可以放入任何类型的变量

        stringList.add("abc");
        stringList.add("def");
        stringList.add(1); //可以添加非 String 类型的变量,编译器不会报错

        for (int i = 0; i < stringList.size(); i++) {
            System.out.println((String) stringList.get(i));
        }
    }
}

上面这段代码在定义stringList时未使用泛型机制,因此可以添加任意类型的变量,即使添加的变量类型不同编译器也不会报错。但!!!在执行代码时会出现错误:

请添加图片描述

这个错误是由于我们错误的将Integer 类型强制转换为String类型而引起的。

当然,这个报错我们可以通过反射机制避免,但这种错误处理方式太过繁琐,而且会大大降低代码的复用性(若通过泛型,可以很轻松的将功能改为存放并输出Integer类型对象,而使用泛型则需要添加新的判断逻辑)。

另外,我们不能寄希望于其他人(甚至自己)会一直按照规定调用代码。因此,最好的方式就是尽可能的在程序编译阶段就将问题暴露出来,而不是等用户真正使用产品的时候再去修改本不该出现的bug。而泛型就有这个能力:通过编译器确保规约得以满足

3. 小结

在这篇博客里,简单概述了一下泛型,接下来将分几篇博客分别介绍泛型类、泛型接口、泛型方法等,随着使用的逐渐深入,对泛型将会有更多的了解。

你可能感兴趣的:(Java,SE学习笔记,java,泛型)