泛型

作者: 覆水无言 | 来源:发表于2018-12-26 18:20 被阅读0次

泛型概述

1:什么是泛型?:

泛型:“参数化类型”,可以从字面理解,参数化,在我们用参数中,定义方法用形参,调用传递实参。类型
在Java中就是指这个类属于那个类。而参数化类型,顾名思义,就是就是将类型定义为参数(可以称之为类型
形参),然后在调用时传入具体类型(类型实参),在你编写一个泛型类时,就可以用在多处。

2:泛型本质:

本质就是参数化类型,也就是在编写代码是不指定具体参数数据的类型,而在使用中指定一个参数,来确定
编写的代码在处理什么类型的数据。这种参数类型可以用在类,接口和方法中,分别被称为泛型类,泛型接口
泛型方法。

3:好处

泛型程序设计:意味着编写的代码可以被很多不同类型的对象所重用。
好的错误检查,让错误在编译期间就暴露出来。

举例:

List arrayList = new ArrayList();
arraylist.add("tian");
arraylist.add(100);
for(int i = 0; i< arraylist.size(); i++){
    String s = (String)arraylist.get(i);
}

上述代码为Java老版本的代码,程序运行时毫无疑问会报错,因为之前的Java list没有对数据类型做限制,它的操作
都是在Object上的,所以一个list结合可以放所有类型的对象,这就造成了很严重的问题,
在Java1.5之后Java团队推出了Java新的能力,泛型,很好的解决了这个问题,之后的代码就变成了这样

List<String> arraylist = new Arraylist<>();
arraylist.add("tian");
arraylist.add(100); //1

在上述代码中,注释1出会直接报错,无法编译,给list集合做了很好的限制,这就是我们需要的泛型。

实例化泛型类型

以List为例,List的源码为泛型接口的定义,
泛型实例化为确定泛型中参数化类型的类型:eg:List<String>
String就实例化了泛型类型。
从上面看出:泛型类有点像普通类的工厂。

泛型通用类

/**
* Box:是一个通用类,
* T:传递给泛型类的类型化参数,他可以采用任何类
* t: 泛型类型T的实例。
*/
public class Box<T> {
    private T t;
}

泛型类型命名约定

上例中T就是泛型的类型化参数,一般可以使用大写字母表示,在实际开发中普遍的命名约定为,
E-element:主要用于Java collentions框架使用。
K-key:主要用来表示地图的参数类型,或map等的K.
V-value:主要表示地图参数和map等的值,可K一起用
N-number:主要表示数字。
T-type:表示一个类中的第一个泛型
S,U,V,和T一样不过分别表示第二,第三,第四个泛型。

特性

泛型只会在编译时有效

List<String> strings = new ArrayList<>();
List<Integer> integers = neww ArrayList<>();
if (strings.getClass().equals(integers.getClass())){
    System.out.println("泛型测试,类型相同");
}
//这里会输出测试日志,

上面的例子可以测试出,Java泛型在编译后会是我们平常看的类,没有泛型标志,也就是说Java泛型只会在编译
阶段有效,编译过程中,正确检查泛型后,会将泛型的相关信息擦除,也就是说泛型不会进入运行阶段,
也就是说,泛型会在逻辑上让人觉得它有许多不同的类型,但实际上都是相同的基本的编码类。

泛型的三种适用方式:泛型类、泛型接口、泛型方法

泛型类:

泛型类就是上述泛型通用类的代码,

/**
* {class 类名 <泛型类型标识>{ 类主体}}
* Box:是一个通用类,
* T:传递给泛型类的类型化参数,他可以采用任何类
* t: 泛型类型T的实例。
*/
public class Box<T> {
    private T t;
}

以T代表泛型类的类型形参,你可以将T看成一个类,这个类是什么类就看你在使用的时候传入的实参了,这个实参
会起到限制作用,只有符合这个实参的操作在会被允许。在不传入实参的情况下这个形参可以是任何类型。

泛型方法

[作用域标识符] <泛型类型标识> [返回类型] 方法名(参数){}

public <T> T getName(T t){
    return t;
}
//当调用泛型方法时,在方法名钱的<>中放入具体的类型
String s = class.<String>getName("song");

泛型接口

public interface DataListener<T>{
    T next();
}

泛型接口定义给出T泛型参数,在实现接口时会有两种情况
1:不指定泛型参数的具体类型,这时需要实现类是泛型类

public class DataListenerImpl<T> implements DataListener<T>{
    @Override
    public T next();
}

2:实现泛型接口时直接指定泛型参数的具体类型,这个可以实现普通类

public class DataListenerImpl implements DataListener<String>{
    @Override
    public String next(){}
}

泛型上下边界

当我们使用泛型时,希望定义的泛型不是被所有的类使用,而是特定的一些类使用时,我们指定泛型的具体类型
这样就违背了泛型的理念,所以泛型还有上下边界的问题,这个上下边界就是限定这个泛型可以被那些类使用。

上下边界的使用

泛型定义时:

<T>:用于定义泛型,类型为未知,类型没有限制.
<T extends classA & interfaceB..>:声明有边界的泛型, 泛型T继承classA,实现接口B。
extends在这里不代表着继承,而是限制的意思,多个参数使用&分割,如果限定中有类,则
类必须放到一个

泛型实例化时(通配符限定)

<?>:标识通配符,用于标识泛型实例化是的类型。
<? extends 父类型>:泛型上边界,用于表示实例化时可以确定的父类型的类型。
<? super 子类型> :泛型下边界,用于表示实例化时可以确定的子类型的类型。

public void keyValue(Apple<? extends Number> obj) {
    //确定Apple内的泛型为继承了Number类的类型。
    System.out.println(" "  + obj.get()) ;
}

泛型擦除

类型擦除就是说Java泛型只能用于在编译期间的静态类型检查,然后编译器生成的代码会擦除相应的类型信息
这样在运行中jvm根本不知道泛型所代表的具体类。因为泛型是1.5版本后引入的,为了保持向下兼容,才有的
类型擦除。

public class Node<T>{
    private T data;
}
//编译中通过了类型检查,编译完成后的代码实际是这样的
public class Node{
    private Object data;
}

这意味着不管我们声明的Node泛型是什么类型,到运行期间都会变成object,

public class Node<T extends Number>{
    private T data;
}
//编译后会是
public class Node{
    private Number data;
}

泛型的限制

由于泛型类的擦除,会存在一些适用限制。

泛型类不能使用基本类型。

基本类型会使泛型编译报错。

泛型不允许进行直接实例化

Java泛型不允许进行静态化

静态变了在类中共享,需要确定的类型,因此编译器无法确定要使用的类型,
所以不允许进行静态化。

class StaticSample<T>{
    private static T t; //编译前类型检查报错
    public static T getT(){ //编译前类型检查报错
        return t;
    }
}

Java泛型不允许直接进行类型转换(通配符可以)

1:以定下泛型类型的变量不能直接进行类型转换。

List<Integer> integers = new ArrayList<Integer>();
List<Double> doubles = new ArrayList<Double>();
integers = doubles; //这个在编译过程中会报错,类型转换出现异常

2:通配符是可以进行转换的

static void cast(List<?> orgin, List<?> dest){
    dest = orgin; //这样是可以进行转换的。
}

Java泛型不允许直接适用instanceof运算符进行运行时类型检查

List<String> strings = new ArrayList<String>();
//这个类型检查是错误的,无法实现。
System.out.println(strings instanceof ArrayList<Double>);

Java泛型不允许创建确切类型的泛型数组

List<Integer>[] list = new ArrayList<Integer>[2];

java泛型不允许作为参数进行重载

因为泛型的擦除,擦除后参数化类就会变的一样,所以无法进行方法重载

public class Test<T>{
    public void test(List<Integer> list){}
    public void test(LIst<Double> list){}
    //这个编译后list会变成一样的,所以不能进行方法重载。
}

相关文章

  • 泛型 & 注解 & Log4J日志组件

    掌握的知识 : 基本用法、泛型擦除、泛型类/泛型方法/泛型接口、泛型关键字、反射泛型(案例) 泛型 概述 : 泛型...

  • 【泛型】通配符与嵌套

    上一篇 【泛型】泛型的作用与定义 1 泛型分类 泛型可以分成泛型类、泛型方法和泛型接口 1.1 泛型类 一个泛型类...

  • 泛型的使用

    泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法 泛型类 泛型接口 泛型通配符 泛型方法 静态方法与...

  • Java 泛型

    泛型类 例如 泛型接口 例如 泛型通配符 泛型方法 类中的泛型方法 泛型方法与可变参数 静态方法与泛型 泛型上下边...

  • 探秘 Java 中的泛型(Generic)

    本文包括:JDK5之前集合对象使用问题泛型的出现泛型应用泛型典型应用自定义泛型——泛型方法自定义泛型——泛型类泛型...

  • Web笔记-基础加强

    泛型高级应用 自定义泛型方法 自定义泛型类 泛型通配符? 泛型的上下限 泛型的定义者和泛型的使用者 泛型的定义者:...

  • 重走安卓进阶路——泛型

    ps.原来的标题 为什么我们需要泛型? 泛型类、泛型接口和泛型方法(泛型类和泛型接口的定义与泛型方法辨析); 如何...

  • Kotlin泛型的高级特性(六)

    泛型的高级特性1、泛型实化2、泛型协变3、泛型逆变 泛型实化 在Java中(JDK1.5之后),泛型功能是通过泛型...

  • Java 19-5.1泛型

    泛型类定义泛型类可以规定传入对象 泛型类 和泛型方法 泛型接口 如果实现类也无法确定泛型 可以在继承类中确定泛型:

  • 【Swift】泛型常见使用

    1、Swift泛型4种 泛型函数泛型类型泛型协议泛型约束 2、泛型约束3种 继承约束:泛型类型 必须 是某个类的子...

网友评论

      本文标题:泛型

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