详谈 java泛型(参数化类型)

一、泛型概念

1、什么是泛型?

泛型,即“参数化类型”。我们最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。

本质:其本质是参数化类型,也就是说所操作的数据类型被指定为一个参数(type parameter)这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
在这里插入图片描述

ArrayList<E> objectName =new ArrayList<>(); 

E: 泛型数据类型,用于设置 objectName 的数据类型,只能为引用数据类型。

2、作用

第一是泛化。可以用T代表任意类型Java语言中引入泛型是一个较大的功能增强不仅语言、类型系统和编译器有了较大的变化,以支持泛型,而且类库也进行了大翻修,所以许多重要的类,比如集合框架,都已经成为泛型化的了,这带来了很多好处。

第二是类型安全。泛型的一个主要目标就是提高Java程序的类型安全,使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果不用泛型,则必须使用强制类型转换,而强制类型转换不安全,在运行期可能发生ClassCast Exception异常,如果使用泛型,则会在编译期就能发现该错误。

第三是消除强制类型转换。泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。

第四是向后兼容。支持泛型的Java编译器(例如JDK1.5中的Javac)可以用来编译经过泛型扩充的Java程序(Generics Java程序),但是现有的没有使用泛型扩充的Java程序仍然可以用这些编译器来编译。

3、优点

1、类型安全
2、消除强制类型转换
3、更高的运行效率
4、潜在的性能收益

二、泛型分类 :泛型类、泛型接口、泛型方法

首先我们得先了解泛型字母的含义

字母意义
E-Element在集合中使用,因为集合中存放的是元素
T-TypeJava类
K - Key
v - Value
N- Number数值类型

首先我们来看看使用泛型跟不使用泛型的区别

不使用泛型:

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @Description: $泛型
 * @Author: dyq
 * @Date: $
 */
public class GenericDemo {
    public static void main(String[] args) {
        show01();
    }

    /*创建集合对象,不使用泛型
    好处:
        集合不使用泛型,默认的类型就是object类型,可以存储任意类型的数据
    弊端:
        不安全,会引发异常
    * */
    private  static  void  show01() {
        ArrayList l = new ArrayList();
        l.add("hi");
        l.add(1122);
        l.add(10f);
        l.add('男');

        //用迭代器遍历集合
        Iterator it = l.iterator();
        while (it.hasNext()){
           /* Object next = it.next();      //输出无异常,以为ArrayList默认的类型就是object类型
            System.out.println(next);*/

            String str = (String) it.next();    //输出异常 ,数组中不单单只有String类型,转型报错
            System.out.println(str);

        }

    }
}

运行输出报错:
在这里插入图片描述
使用泛型
当规定泛型的类型时,输入非规定类型数据会直接报错。
在这里插入图片描述

import java.util.ArrayList;
import java.util.Iterator;
/**
 * @Description: $泛型
 * @Author: dyq
 * @Date: $
 */
public class GenericDemo {
    public static void main(String[] args) {
        show01();
    }
    /*创建集合对象,使用泛型
    好处:
       1 .避免了类型转换的麻烦,存储的是什么类型,取出的就是什么类型
       2.把运行期异常(代码运行之后会批出的异常),提升到了编译期
    弊端:
       泛型是什么类型,只能存储什么类型的数据
     */
    private  static  void  show01() {
        ArrayList<String> l = new ArrayList<>();
        l.add("小马");
        l.add("小明");
        l.add("小华");
        l.add("小红");
        //用迭代器遍历集合
        Iterator it = l.iterator();
        while (it.hasNext()){
            String str = (String) it.next();    
            System.out.println(str);
        }
    }
}

输出结果:

小马
小明
小华
小红

1、泛型类

定义格式:

修饰符 class类名<代表泛型的变量>{}

看一个指定泛型为String 的

public class GenericClass {
    private  String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public static void main(String[] args) {
        GenericClass gc =new GenericClass();
           gc.setName("我只能是字符串");
           
           String name = gc.getName();
           System.out.println(name);
    }
}

输出
在这里插入图片描述
当我们使用不使用指定类型时

/**
 * @Description: $定义一个含有泛型的类, 模拟Arraylist集合
 * 泛型是一个未知的数据类型,当我们不确定什么什么数据类型的时候,可以使用泛型
 * 泛型可以接牧任意的数据类型,可以使用Integer , String, student.. .
 * 创建对象的时候确定泛型的数据类型
 * @Author: dyq
 * @Date: $
 */
public class GenericClass<E> {
    private  E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }

    public static void main(String[] args) {
        //不写泛型默认为Object
        GenericClass gc =new GenericClass();
           gc.setName("我是Object");
           Object name = gc.getName();
           System.out.println(name);

        //可以通过创建对象时确定数据类型
        //泛型使用Integer类型
        GenericClass<Integer> Igc = new GenericClass<>();
        Igc.setName(1);
        Integer i = Igc.getName();
        System.out.println("我是Interger类型:"+i);

        //泛型使用String类型
        GenericClass<String> sgc = new GenericClass<>();
        sgc.setName("花花");
        String s = sgc.getName();
        System.out.println("我是String类型:"+s);
    }
}

结果:

我是Object
我是Interger类型:1
我是String类型:花花

2、泛型方法

定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间格式:

语法

修饰符<泛型>返回值类型方法名(参数列表(使用泛型)){
方法体;
}

代码示例:

/**
 * @Description: 泛型方法$ 含有泛型的方法,在调用方法的时候确定泛型的数据类型,
 * 传递什么类型的参数,泛型是什么类型
 * @Author: dyq
 * @Date: $2021年2月5日
 */
public class GenericMethod {
    //定义一个含有泛型的方法
    public <M> void  method01(M m){
        System.out.println(m);
    }
    //定义一个含有泛型的静态方法
    public  static <S> void method02(S s){
        System.out.println(s);
    }

    //测试含有泛型的主方法
    public static void main(String[] args) {
        GenericMethod m = new GenericMethod();
        m.method01(10);
        m.method01("你好");
        m.method01('男');
        m.method01(19);

        System.out.println("===============");
        m.method02("我是方法02");
        m.method02("ok!");
        m.method02(1);
        m.method02('女');
    }
}

结果:

10
你好
男
19
===============
我是方法02
ok!
1

3、泛型接口

定义格式∶

修饰符interface接口名<代表泛型的变量>{ }

代码演示:
新建一个接口

/**
 * @Description: 泛型接口$
 * @Author: dyq
 * @Date: 2021年2月5日$
 */
public interface GenericInterface<E> {

    public abstract  void add(E e);

    public abstract E getE();
}

有两种实现方法先创建一个GenericInterfaceImpl01类

/**
 * @Description: $含有泛型的接口,第一种方式:定义接口的实现,实现接口,指定接口的泛型
 * @Author: dyq
 * @Date: $
 */
public class GenericInterfaceImpl01 implements GenericInterface<String>{

    @Override
    public void add(String s) {
     System.out.println(s);
    }

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

再创建一个GenericInterfaceImpl02实现类

/**
 * @Description: $含有泛型的接口第二种使用方式:接口使用什么泛型,
 * 实现类就使用什么泛型,类跟着接口走就相当于定义了一个含有泛型的类,
 * 创建对象的时候确定泛型的类型
 * @Author: dyq
 * @Date: $
 * public interface List<E>{
 *     boolean add(E e);
 *     E get(int index);
 *     }
 * public class ArrayList<E> impLements List<E>{
 * }
 */
public class GenericInterfaceImpl02<I> implements GenericInterface<I> {
    @Override
    public void add(I i) {
        System.out.println(i);
    }

    @Override
    public I getE() {
        return null;
    }
}

最后新建一个测试类

/**
 * @Description: $测试含有泛型的接口
 * @Author: dyq
 * @Date: $
 */
public class GenericInterfaceTest {
    public static void main(String[] args) {
        //创建GenericInterfaceImpl
        GenericInterfaceImpl01 gil =new GenericInterfaceImpl01();
        gil.add("哈哈哈~");

        //创建GenericInterfaceImpl01  定义输出的数据类型
        GenericInterfaceImpl02<Integer> gil2 = new GenericInterfaceImpl02<Integer>();
        gil2.add(10);

        GenericInterfaceImpl02<Double> gil3 = new GenericInterfaceImpl02<Double>();
        gil3.add(8.88);

    }
}

结果:

哈哈哈~
10
8.88

三、泛型的通配符

当使用泛型类或者接口时,传递的数据中,泛型类型不确定,可以通过通配符< ?>表示。但是一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。

1、通配符基本使用

泛型的通配符:不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。此时只能接受数据,不能往该集合中存储数据。
代码示例:

import java.util.ArrayList;
import java.util.Iterator;

/**
 * @Description: $类型通配符一般是使用?代替具体的类型参数
 * @Author: dyq
 * @Date: $
 */
public class GenericTest {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);

        ArrayList<String> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("b");

        printArray(list);
        printArray(list1);
        
    }
    
    /*定义一个方法,能遍历所有类型的ArrayList集合
    * 这时候我们知道ArrList集合使用说明数据,可以泛型的通配符?来接收数据类型*/

    public  static  void printArray(ArrayList<?> list){
        //使用迭代器遍历集合
        Iterator<?> it =list.iterator();
            while (it.hasNext()){
                //it.next()方法。取出的元素是object,可以接收任意的数据类型
                Object o = it.next();
              System.out.println(o);
        }
    }
}

结果:

1
2
a
b

2、类型通配符上限通过形如List来定义,如此定义就是通配符泛型值接受Number及其下层子类类型。
3、类型通配符下限通过形如 List<? super Number>来定义,表示类型只能接受Number及其三层父类类型,如 Object 类型的实例。
代码示例:

import java.util.ArrayList;
import java.util.Collection;

/**
 * @Description: $泛型的上线限定:? extends E  代表我使用的泛型只能是E类型/本身
 *                泛型的下限定: ? super E   代表我使用的泛型只能是E类型/本身
 * @Author: dyq
 * @Date: $
 */
public class GenericTest01 {
    public static void main(String[] args) {
        Collection<Integer> list1 = new ArrayList<Integer>();
        Collection<String> list2 = new ArrayList<String>();
        Collection<Number> list3 = new ArrayList<Number>();
        Collection<Object> list4 = new ArrayList<Object>();
        
        getElement1(list1);
        getElement1(list2); //报错
        getElement1(list3);
        getElement1(list4);//报错

        getElement2(list1);//报错
        getElement2(list2);//报错
        getElement2(list3);
        getElement2(list4);
        
        /*类与类之间的继承关系
        Integer extends Number extents Object
        STring extends Object
        * */
    }
    //泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
    public static  void  getElement1(Collection<? extends  Number> coll){
    }
    //泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
    public  static  void  getElement2(Collection<? super  Number> collection){}
}

会有报错
在这里插入图片描述

  • 12
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北街风

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值