美文网首页
Java泛型知识点整理

Java泛型知识点整理

作者: EvanZch | 来源:发表于2019-12-19 21:48 被阅读0次

一、什么是泛型

泛型就是参数化类型,即我们在定义的时候,将具体的类型进行参数化,在调用或者使用的时候,再传入具体的参数类型,我们可以将泛型用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

二、为什么要使用泛型

泛型在开发过程中经常出现,比如我们一直高频使用的List集合,我们可以这么创建一个 ArrayList 集合。

List<String> stringList = new ArrayList<>();

再看一下List的定义

public interface List<E> extends Collection<E>{...}

我们看到在List接口后面加了 <E> 对类型进行参数化,及参数是可变的,需要我们在使用的时候传入实际的参数,这样的好处是什么?

我们可以看看这么一个例子:

image

当我们对List 添加泛型后,我们在使用的时候,可以根据需求声明不同类型的实际参数,如果我们传入的String,我们对List集合添加数据的时候,很明显看到当添加的数据不是String类型的时候,编译器会报错,当我们在获取数据的时候,也不需要强制类型转换,会自动返回我们传入实际参数的类型。

image

如果我们不传入实际类型,会出现什么情况?

image

我们定义的List集合没有指定明确的参数,在数据添加的时候,可以看到我们可以添加不同类型的参数,编译器也没有报错,在获取数据的时候,返回的是Object类型,所以我们需要对其进行强制转换,而这个过程,很容易就会报ClassCastException异常。

所以,我们使用泛型的好处有:

1、适用于多种数据类型执行相同的代码

2、类型安全:我们使用泛型的时候,指定了特定的参数类型,这样对其类型进行限定,可以在编译期间对我们的传入的参数类型进行判断,增加了类型的安全性。

3、取消强制类型转换:我们指定明确的类型参数后,由于在编译阶段就会对类型进行约束,泛型会自动且隐式的给我们做类型转换,转换成我们指定的类型,我们不再需要关心类型的转换。

三、泛型的使用

泛型可以定义在类、接口、方法上,分别被称为泛型类、泛型接口、泛型方法。

3.1、泛型类

通过 <> 将类型变量T(大写字母都可以,不过常用的就是T,E,K,V等等)括起来,放在类名后面,泛型类运行有多个类型变量。

一个类型变量的泛型类:

/**
 * @author : EvanZch
 *         description: 一个类型变量的泛型类
 **/
public class genericClassTest<T> {
    private T mData;

    public genericClassTest() {
    }

    public T getmData() {
        return mData;
    }

    public void setmData(T mData) {
        this.mData = mData;
    }
}

多个类型变量的泛型类:

/**
 * @author : EvanZch
 * description: 两个类型变量的泛型类
 **/
public class genericClassTest1<T, K> {
  // ...
}

3.2、泛型接口

泛型接口的定义和泛型一致

/**
 * @author : EvanZch
 *         description: 泛型接口
 **/
public interface genericInterface<T> {

    T getData();

    void set(T data);
}

我们在实现泛型接口可以使用下面两种方式

1、不指定具体的泛型实参

/**
 * @author : EvanZch
 *         description:实现泛型接口,不指定具体类型
 **/
public class genericInterfaceImpl1<T> implements genericInterface<T> {

    @Override
    public T getData() {
        return null;
    }

    @Override
    public void set(T data) {

    }
}

// 我们在调用的时候,再传入实际类型
genericInterfaceImpl1<String> interfaceImpl1 = new genericInterfaceImpl1();

2、指定具体的泛型实参

/**
 * @author : EvanZch
 *         description:实现泛型接口,指定具体类型
 **/
public class genericInterfaceImpl2  implements genericInterface<String> {

    @Override
    public String getData() {
        return null;
    }

    @Override
    public void set(String data) {

    }
}

// 使用时候,直接创建
genericInterfaceImpl2 genericInterfaceImpl2 = new genericInterfaceImpl2();

3.3、泛型方法

泛型方法,是在调用方法的时候指明泛型的具体类型 ,泛型方法可以在任何地方和任何场景中使用,包括普通类泛型类。注意泛型类中定义的普通方法和泛型方法的区别。

image

我们区别一下普通的方法和泛型方法

image

我们看到普通方法中也使用了泛型,但是它只是一个普通的方法,只是它的返回值和传入的类型是在前面已经声明过得泛型,所以,这里才可以继续使用 T 这个类型变量。

而下面这个泛型方法,首先通过 <E> 标识了它是一个泛型方法,返回值类型和传入的类型一致,通过泛型进行参数化了。

四、泛型类型变量的限制

我们使用泛型的时候,对类型进行参数化,使用的可以传入任意的类型,但是在实际使用过程中,我们可能需要对类型进行限制,在进行类型擦除的时候,会转换成我们限定的类型,比如我们要比较两个字符的大小,需要用到 compareTo 方法

int compareTo(Object o) 或 int compareTo(String anotherString)

返回值是整型,它是先比较对应字符的大小(ASCII码顺序),如果第一个字符和参数的第一个字符不等,结束比较,返回他们之间的差值,如果第一个字符和参数的第一个字符相等,则以第二个字符和参数的第二个字符做比较,以此类推,直至比较的字符或被比较的字符有一方结束。

  • 如果参数字符串等于此字符串,则返回值 0;
  • 如果此字符串小于字符串参数,则返回一个小于 0 的值;
  • 如果此字符串大于字符串参数,则返回一个大于 0 的值。
image

可以看到如果我们直接这么定义,会报错,因为T为任意类型,但是 compareTo 方法定义在Comparable 接口中,我们需要限定传入的类型必须实现了 Comparable 接口

public interface Comparable<T> {
    public int compareTo(T o);
}

我们可以这么写:

image

这里我们对类型对进行了限定,使用了通配符和指明了上界 ? extends X ,这样就限制了我们传入的参数类型必须是实现了Comparable接口,我们在调用的时候,编译器就会进行判断,我们传入的参数是否实现了Comparable接口,如果没有就会报错。

Integer 实现了 Comparable 接口

public final class Integer extends Number implements Comparable<Integer>

image

五、泛型通配符

上面类型变量的限制中,我们使用了通配符 ? ,通配符的有三种使用方式。
<? extends X> : 类型的上界限定,参数类型是X的子类。
<? super X> :类型的下界限定,参数类型是X的超类。

<?> : 无界限定。

为了说明他们的区别,我们先新建People类、Man类、Son类,其中Man继承至PeopleSon 继承至 Man

People类:

public class People {
}

Man 类:

public class Man extends People {
}

Son 类:

public class Son extends Man {
}

再创建一个泛型类 Test<T>

public class Test<T> {
    
    private T data;

    public T getData() {
        return data;
    }

    public void setData(T mData) {
        this.data = mData;
    }
}

查看继承关系:

image

很明显,我们可以看到People类和Man类是具有继承关系的,但是 Test<People>Test<Man> 之间却没有任何关系。

我们进行如下操作

image

可以看到set,get都没问题,因为我们将泛型直接指定为 People,而 ManSon 都是people的子类,所以我们都能set进去。

1、? extends X : 上界限定通配符

我在再使用 ? extends X ,指明类型的上界限定为 X,表示传入方法的类型参数必须是其本身或子类。

image

但是对于泛型类来说,如果内部提供了get\set方法,那么set不允许调用,即类型的上界只读。

image

我们可以看到,我们通过 <? extends People> 指明了类型参数上界为 People,那我们执行取操作的时候,即调用get的时候,编译器知道返回的肯定是个 x(不管是x还是x的子类),但是我们进行设置的时候,编译器只知道我们传入的是 x ,至于具体是 x 的那个子类并不知道,所以没有办法进行set操作。

总结:上不存。

主要用于安全地访问数据,可以访问X及其子类型,并且不能写入非null的数据。

2、? super X : 下界限定通配符

使用 ? super X ,指明类型的下界限定为 X,表示传入方法的类型参数必须是其本身或其超类。

image

但是对于泛型类来说,如果内部提供了get\set方法,那么set只能传入X的子类及其本身,get返回的类型是Object。

image

? super X 表示类型的下界,类型参数是X的超类(包括X本身),那么可以肯定的说,get方法返回的一定是个X的超类,那么到底是哪个超类?不知道,但是可以肯定的说,Object一定是它的超类,所以get方法返回Object。编译器是可以确定知道的。对于set方法来说,编译器不知道它需要的确切类型,但是X和X的子类可以安全的转型为X。

总结:下不取

主要用于安全地写入数据,可以写入X及其子类型。

3、<?> 无限定通配符

表示类型无限制,和 T 的区别

ArrayList<T> al=new ArrayList<T>(); 指定集合元素只能是T类型

ArrayList<?> al=new ArrayList<?>(); 集合元素可以是任意类型,这种没有意义,一般是方法中,只是为了说明用法。

总结

泛型在java语言中的一种语法糖的存在,java中泛型的实现为类型擦除,是一种伪泛型。

相关文章

  • Java泛型

    参考:Java知识点总结(Java泛型) 自定义泛型类 自定义泛型接口 非泛型类中定义泛型方法 继承泛型类 通配符...

  • 死磕Java泛型(一篇就够)

    Java泛型,算是一个比较容易产生误解的知识点,因为Java的泛型基于擦除实现,在使用Java泛型时,往往会受到泛...

  • Java泛型-你可能需要知道这些

    本博文为Java泛型扫盲文,争取读完后能理解泛型并使用泛型。 1. 几个知识点 1.1 什么是泛型 泛型的本质是参...

  • Java泛型知识点整理

    一、什么是泛型 泛型就是参数化类型,即我们在定义的时候,将具体的类型进行参数化,在调用或者使用的时候,再传入具体的...

  • Android 知识体系整理

    基础的知识点全部略过,下面都为进阶知识点 Java 篇 1. 泛型 熟练掌握泛型语法是在java开发中涉及到的架构...

  • java泛型详解

    泛型是Java中一个非常重要的知识点,在Java集合类框架中泛型被广泛应用。本文我们将从零开始来看一下Java泛型...

  • 详解Java泛型之4——一个例子理解泛型带来的好处

    前面我介绍了关于泛型、通配符以及泛型擦除的相关知识点,大家可以参考以下文章: 详解Java泛型之1——入门泛型必懂...

  • Java泛型教程

    Java泛型教程导航 Java 泛型概述 Java泛型环境设置 Java泛型通用类 Java泛型类型参数命名约定 ...

  • 详解Java泛型之3——十分钟理解泛型擦除

    前面我们介绍了泛型以及通配符的基础概念,可以参考文章: 详解Java泛型之1——入门泛型必懂的知识点[https:...

  • Java泛型使用示例整理

    Java泛型使用示例整理 目标 Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用...

网友评论

      本文标题:Java泛型知识点整理

      本文链接:https://www.haomeiwen.com/subject/hmbvnctx.html