Java基础--泛型详解

一、背景

java推出泛型之前,集合元素类型可以是object类型,能够存储任意的数据类型对象,但是在使用过程中,如果不知道集合里面的各个元素的类型,在进行类型转换的时候就很容易引发ClassCastException异常。

二、概念 

java泛型是jdk5中引入的一种新特新,泛型提供了编译时类型安全监测机制,能够在编译期间检查到非法的类型数据结构。其本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。

三、泛型类、接口 

一、泛型类,实例化类的时候指明泛型的具体类型

1、泛型类的定义模版

Class 类名称 <泛型标识,泛型标识,...>{

        private 泛型标识  变量名;

        .............................

}

2、常用的泛型标识:T、E、K、V

3、使用

类名<具体的数据类型> 对象 = new 类名<具体数据类型,jdk1.7后可以是空的>(); 

4、注意 

1、泛型在创建对象的时候,没有指定类型,将按照Object类型来操作

2、泛型类不支持基本数据类型

3、同一泛型类,根据不同的数据类型创建的对象,本质上是同一个类型。都是这个泛型类的对象,只是里面的泛型做代表的类型不同。

二、从泛型类派生子类

1、子类也是泛型类,子类和父类的泛型类型要一致。

Generic<E> // 这里可以是E,但是下面子类和继承父类的泛型需要一致

class  ChildGeneric<T> extends Generic<T>

// 不能子类是T,这里给父类就是E,这表示两个类型是不一致的。原理就是创建子类的时候会先创建父类,如果一致,那么会进行类型传递,告诉父类创建对应类型,如果不一致,将无法得到具体类型。

2、子类不是泛型类,父类要明确泛型的数据类型 

class  ChildGeneric extends Generic<String>

// 这里需要明确父类数据类型,主要是子类不是泛型类型,无法传递给父类,告知父类泛型该是何种数据类型,所以需要在子类明确

三、泛型接口

1、泛型接口的定义模版

interface 接口名称<泛型标识,泛型标识,........>{

        泛型标识  方法名();

        ..........

}

2、使用

1、实现类不是泛型类,接口需要明确数据类型。原理与不是泛型类的子类实现泛型父类一致。如果不明确,默认就是object类型。

2、实现类是泛型类,实现类与泛型接口的泛型类型要一致。

四、泛型方法

一、语法

修饰符 <T,E,.....> 返回值类型   方法名(参数){

        方法体

}

1、修饰符与返回值中间的泛型类型很重要,这个表示声明此方法为泛型方法。

2、  只有在修饰符与返回值中间处声明了<T,E,.....>的方法才是泛型方法,泛型类中使用了泛型的成员方法并不是泛型方法。

3、 <T,E,.....>表示该方法使用泛型,T,E,....,此时才可以在方法中使用对应的泛型类型。

4、如果泛型类上的泛型是T,泛型方法是上面这样声明的,使用的泛型参数也是T,那么在使用时泛型类传入的具体类型,和调用方法时传入的具体类型是没有关系的,相当于这两个T代表的是不同的泛型。也就是说调用时泛型类传入的是Integer,而泛型方法可以传入String,然后用的也是String类型。

5、泛型方法可以使用static,但是泛型类的成员方法,如果使用了泛型类的泛型,则不能使用stiatic,这是因为泛型类型参数是与类的实例相关联的,而不是与类本身相关联的。而static方法是与类相关联的,它们不依赖于类的实例。

public class MyGenericClass<T> {
    private T value;

    public MyGenericClass(T value) {
        this.value = value;
    }

    // 这是一个非静态的泛型方法
    public T getValue() {
        return value;
    }

    // 这是一个静态方法,但不能使用泛型类型参数T
    public static void staticMethod() {
        // 在静态方法中不能引用泛型类型参数T
        // 下面的代码将会编译错误
        // T item = value; // 错误
    }
}
 

在上面的例子中,getValue方法是一个非静态的泛型方法,因为它依赖于类的实例中的泛型参数。而staticMethod是一个静态方法,它无法直接访问泛型类型参数T,因为它与类的实例无关。在静态方法中,你无法访问类的实例变量,因此也无法访问与实例相关的泛型参数T。

二、泛型方法与可变参数 

修饰符  static <E> 返回值类型   方法名(E...  e ){

        方法体

}

五、类型统配符 

一、概述

类型通配符一般是使用“?”代替具体的类型实参,所以通配符是类型实参,而不是类型形参。

二、类型通配符上限

1、语法

类/接口<? extends 实参类型>:要求该泛型的类型,只能是实参类型或者是实参类型的子类类型。

实参形参具体可看:Java方法参数的形参和实参_java 函数 形参-CSDN博客

2、注意

public void test(ArrayList<? extends cat> list){

        1、 这里需要注意 list在方法体内是无法添加元素的,这是由于传入的不知道是cat还是cat的子类,那添加元素就无法确定添加那种数据,假如说传入的是cat的子类A,这个时候我们并不知道,就有可能添加cat的子类B。

        2、这里可以使用cat来接受元素类型

}

三、类型通配符的下限 

1、语法

类/接口<? super 实参类型>:要求该泛型的类型,只能是实参类型或者是实参类型的父类类型。

2、 注意

public void test(ArrayList<? super cat> list){

        1、这里需要注意 list可以在方法体内添加元素,通配符下限并不约束元素类型。

        2、遍历时使用的是Object来接收元素

}

六、类型擦除

一、无限制擦除 

一般是使用除extends的上限通配符,其他的泛型类型都会在编译期间进行类型擦除,将泛型转为Object类型。

二、有限制擦除 

使用了泛型的上限通配符时,在编译期间进行类型擦除,将泛型转为上限类型。

三、桥接方法

七、泛型数组

一、可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象。例如:

ArrayList<String>[] list = new ArrayList<String>[];  //  这样是会编译报错的

ArrayList<String>[] list = new ArrayList[];  // 只能这样,创建一个非泛型数组对象,再赋值给list变量。

出现这个现象的原因就是:泛型会在编译期间进行类型擦,而数组刚好相反,在编译期间也会一直持有对应的类型,所以这两种一开始就是对立的。

 二、可以通过java.lang.reflect.Array的newInstance(Class<T> class,int length)方法创建泛型数组。

八、泛型和反射

常用:Class<T>和Constructor<T>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值