<strong id="8cmf8"><pre id="8cmf8"></pre></strong><progress id="8cmf8"><track id="8cmf8"></track></progress>

<button id="8cmf8"><object id="8cmf8"></object></button>

    <button id="8cmf8"><acronym id="8cmf8"></acronym></button>

  1. <button id="8cmf8"><acronym id="8cmf8"></acronym></button>

        当前位置: 首页 / 技术分享 / 正文
        民哥带你快速精通java泛型(一)

        2022-10-18

        泛型 类型   

          泛型由入门到精通

          Hi,小伙伴你好~欢迎进入泛型的学习,在学习之前友情提醒一下:学习泛型需要小伙伴们具备一定的javaSE基础,如果之前小伙伴们没有接触过java,大家可以移步到千锋北京java好程序员的javaSE课程进行学习。

          在正式开始学习之前,我们先来看一段经常书写的代码,分析一下代码存在那些问题?

          代码如下:

        public class GenericsDemo {
        public static void main(String[] args) {
        //1.创建一个List对象
        List list = new ArrayList();
        //2.向List中添加数据
        list.add("python");
        list.add("java");
        list.add(66);
        //3.遍历集合
        for (int i = 0; i <list.size() ; i++) {
        //4.把集合中的每个元素转成String类型
        String ele = (String) list.get(i);
        //5.打印-测试结果
        System.out.println("元素的值:"+ele);
        }
        }
        }

          运行代码,会报如下图的异常:

        a1

          那么小伙们,我们来分析一下原因,到底是因为什么报这个异常呢?

          在代码中我们定义了一个List类型的集合,先向其中加入了两个String类型的值,随后加入一个Integer类型的值。这是完全允许的,因为此时list默认的类型为Object类型,所以在代码编译期间没有任何问题。

          但是在运行代码时,由于list集合中既有String类型的值,又有Integer类型的值,致使list集合无法区分值是什么类型,很容易出现上图中的错误。因为编译阶段正常,而运行时会出现“java.lang.ClassCastException”异常。因此导致此类错误编码过程中不易发现。

          分析完了,小伙们现在明白了吧,通过分析我们发现上述代码主要存在两个问题:

          当我们将数据存入集合时,集合不会记住数据的类型,默认数据类型都是Object。

          当我们遍历集合中的数据时,人为进行强制类型转换,很容易报“java.lang.ClassCastException”。强制类型转换异常

          那么有没有什么办法可以使集合能够记住集合内元素各类型,且能够达到只要编译时不出现问题,运行时就

          不会出现“java.lang.ClassCastException”异常呢?

          答案就是使用泛型。

          那么什么是泛型呢?

          第一关 让我们一起走入泛型

          1.泛型的概述

          1.1 什么是泛型

          泛型,泛指任意类型,可以应用在接口上,类上,变量上,方法上,以及方法的参数中。

          百度百科介绍:

          泛型是jdk1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

          在jdk1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况 ,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

          泛型的好处:使用泛型,首先可以通过IDE进行代码类型初步检查,然后在编译阶段进行编译类型检查,以保证类型转换的安全性;并且所有的强制转换都是自动和隐式的,可以提高代码的重用率。

          个人理解:

          简单来说:泛型,即“参数化类型”,那么类型“参数化”到底怎么理解呢?

          顾名思义,类型“参数化”就是将类型由原来的具体类型,变成参数化的“类型”,有点类似于方法中的变量参数,不过此时是类型定义成参数形式(你可以理解为类型形参),然后在使用时传入具体的类型(也就是类型实参)。为什么这样操作呢?因为它能让类型"参数化",也就是在不创建新的类型的情况下,通过泛型可以指定不同类型来控制形参具体限制的类型。

          总结:

          泛型介绍完了, 小伙伴看完上述解释后,能理解泛型是什么了吗?我们可以用两句话来概括一下:

          泛型在声明时,用标记符表示,仅仅作为“参数化的类型”,可以理解为形式参数。

          比如:

          //定义List集合时,用标记符E表示任意类型,E可以理解为形式参数,没有具体的类型值public interface Listextends Collection{ ----------}

          泛型在使用时,需要指定具体的类型,也就是类型实际的参数。

          比如:

          //在使用List集合时,需要确定E的具体类型,String可以理解为具体的类型,也就是实际的参数值Listlist = new ArrayList();

          1.2 常用的泛型标记符

          E - Element (集合使用,因集合中存放元素)

          T - Type(Java 类)

          K - Key(键) V - Value(值)

          N - Number(数值类型)

          ? - 表示不确定的java类型

          S、U、V - 2nd、3rd、4th types

          你可能会有疑问,弄这么多标识符干嘛,直接使用万能的Object难道不香么?我们知道Object是所有类的基类(任何类如果没有指明其继承类,都默认继承于Object类),因此任何类的对象都可以设置Object的引用,只不过在使用的时候可能要类型强制转换。但是如果设置了泛型E、T等这些标识符,那么在实际使用之前类型就已经确定,因此不再需要类型强制转换

          2.泛型的语法使用

          2.1 泛型在集合中的使用

          单列集合中List中

        public interface List<E> extends Collection<E> {
        ----
        <T> T[] toArray(T[] a);
        ----
        }

          双列集合Map中

        public class HashMap<K,V> extends AbstractMap<K,V>
        implements Map<K,V>, Cloneable, Serializable {
        ---
        V get(Object key){---};
        V put(K key, V value){---};
        V remove(Object key){---};
        void putAll(Map<? extends K, ? extends V> m){---};
        -----
        }

          小伙们可以看到:List,HashMap的源码,在声明集合时或者定义方法时,使用采用尖括号内加占位符的形式 ,这里的占位符就是我们上面说的泛型标记符,泛型标记符号E,K,V,T等用来表示任意类型(E,K,V,T也就是“泛型形参”,在实例化集合对象时需要明确的具体的类型(也就是“泛型的实际参数”))。

          通过观察集合的源码,那么我们自己也可以定义泛型接口,泛型类以及泛型方法,下面我们一起操作一下吧。

          2.2 声明泛型接口

          泛型应用于接口。例如生成器(GeneratorType),这是一种专门负责创建对象的类。当使用生成器创建新的对象时,它不需要任何参数,也就是说生成器无需额外的信息就知道如何创建新对象。

          一般而言,一个生成器只定义一个方法,该方法用以产生新的对象。

        /**
        * 定义一个泛型接口:生成任意对象
        * @param <T>: 泛型形式参数,可以是任意类型
        */
        public interface GeneratorType<T> {
        T create();
        }

        /**
        * 测试泛型接口
        */
        class DemoGeneratorType{
        public static void main(String[] args) {
        //1.使用生成器:创建random对象
        GeneratorType<Random> gt= new GeneratorType<Random>() {
        @Override
        public Random create() {
        return new Random();
        }
        };
        //2.使用 GeneratorType:创建对象
        Random random = gt.create();
        }
        }

          来,小伙伴们,我们一起分析下上面的代码:

          我们声明了一个泛型接口 GeneratorType,目的用来生成任意类型的对象,在这里T可以表示任意类型。

          我们在测试类中,通过GeneratorType创建对象时,可以传递任意类型。

          比如 GeneratorType,那么就可以生成Random对象了

          注意: 在这里,我们通过匿名内部类的方式创建了Random对象,这种写法大家要慢慢熟悉喔。

          2.3 声明泛型类

          泛型应用于类上面。例如订单类(Order),这是一个专门负责封装订单里面商品的类,当我们购物生成订单时,订单里面可以包含任何商品信息。

          请注意,在类上定义的泛型,在类的变量、方法的参数以及方法中同样也能使用(静态方法除外)。

        /**
        * 定义一个订单类:封装任意类型的商品信息
        * @param <T>
        */
        public class Order<T> {
        private T t ;//在变量中使用: T表示任意商品类型
        public T get(){//在普通方法中使用:T表示任意商品类型
        return t;
        }
        public void set(T t){//在方法的参数使用: T表示任意类型
        this.t = t;
        }
        //测试:
        public static void main(String[] args) {
        Order<Phone> order = new Order<Phone>();//创建订单对象:封装Phone商品
        order.set(new Phone("华为Mate20",3899.0));
        System.out.println("商品名称:"+order.get().getPhoneName());
        }
        }
        //定义手机商品类
        class Phone{
        private String phoneName;
        private Double phonePrice;

        public Phone(String phoneName, Double phonePrice) {
        this.phoneName = phoneName;
        this.phonePrice = phonePrice;
        }

        public Phone() {
        }

        public String getPhoneName() {
        return phoneName;
        }

        public void setPhoneName(String phoneName) {
        this.phoneName = phoneName;
        }

        public Double getPhonePrice() {
        return phonePrice;
        }

        public void setPhonePrice(Double phonePrice) {
        this.phonePrice = phonePrice;
        }
        }

          ok,泛型类我们声明完成了,大家看一下是不是和我们声明泛型接口很相似啊,确实是一样的。

          声明的语法就是:类名,在这里T可以表示任意类型。

          小伙伴也可以看到,我们定义了一个带泛型的Order类,在我们创建订单对象时,可以传入任意类型的商品对象,使我们的操作更加灵活

          2.4 声明泛型方法

          泛型应用于方法上面。前面说过在泛型类上定义的泛型,在类的方法中也能使用(静态方法除外)。但是有的时候我们只想在某个方法上使用泛型,而不是整个类,这也是被允许的,下面我和小伙们一起来体验一下。

          比如FactoryBean工厂类,通过泛型方法,创建任意类型的对象。

        package cn.qf;
        /**
        * 定义一个工厂Bean:
        */
        public class FactoryBean {
        /*
        定义不带泛型的方法
        */
        public static Object createObject0(String className) throws Exception{
        return Class.forName(className).newInstance();
        }
        /*
        定义一个普通的泛型方法:className表示类的全路径
        */
        public <T> T createObject1(String className) throws Exception{
        return (T) Class.forName(className).newInstance();
        }
        /*
        定义一个静态的泛型方法:className表示类的全路径
        */
        public static <T> T createObject2(String className)throws Exception{
        return (E) Class.forName(className).newInstance();
        }
        //测试:
        public static void main(String[] args) throws Exception {
        //创建一个Phone对象 : 不使用泛型方法,需要类型强转
        Phone p1 = (Phone) FactoryBean.createObject0("cn.qf.Phone");
        //创建一个Phone对象 :泛型方法,不需要类型强转
        Phone p2 = FactoryBean.createObject2("cn.qf.Phone");
        }
        }

        class Phone{
        private String phoneName;
        private Double phonePrice;
        ----
        }

          在这里我们使用工厂模式来创建对象,为了在我们获取对象时,不用类型强转,我们也使用了泛型。小伙伴通过代码可以看到,不使用泛型的方法,在获取对象时,需要类型强转(可能会引起类型强转异常)。

          在使用泛型方法获取对象时,不需要类型强转(可以避免引起类型强转异常)。

          2.5 泛型方法、泛型接口、泛型类小结

          从上面的介绍小伙伴也看到了,泛型类的好处就是在泛型类上定义的泛型,在类的方法中也能使用(普通静态方法除外)。而泛型方法的最大优点就是能独立于类,不受类是否是泛型类的限制。因此当你考虑使用泛型的时候,优先考虑定义泛型方法。如果非要定义泛型类,

          个人建议通过使用泛型方法来将整个类泛型化,因为这样就不用担心静态方法的事,如果有静态方法那必然是泛型方法。这样就能避免普通静态方法无法获取泛型类泛型的尴尬局面。

          你以为这就把泛型介绍完了吗?并没有,小伙伴们先休息片刻,稍后我们继续喔。

          闯关练习

          需求:

          定义一个泛型类:

          包含与类的泛型一样的变量,

          包含与类的泛型一样的方法,参数也使用泛型

          同时定义一个类的泛型不相同的泛型方法

          答案:

        /**
        * 定义泛型类:
        * @param <T>: 泛型T
        */
        public class GenericDemo4<T> {
        //1.定义一个与T 一样的变量
        private T t;
        //2.定义一个与T一样的方法
        public T test1(T outer){
        System.out.println(outer);
        return outer;
        }
        //3.定义一个与T不一样的方法
        public <E> E test2(E e){
        System.out.println("自定义泛型的方法:"+e);
        return e;
        }
        //测试:
        public static void main(String[] args) {
        //1.创建对象:指定T的泛型为 String
        GenericDemo4<String> gt = new GenericDemo4<String>();
        //2.调用 与T 一样的泛型方法
        gt.test1("hello world");
        //3.调用 与T 不一样的泛型方法
        gt.test2(10);
        }
        }

        好程序员公众号

        • · 剖析行业发展趋势
        • · 汇聚企业项目源码

        好程序员开班动态

        More+
        • HTML5大前端 <高端班>

          开班时间:2021-04-12(深圳)

          开班盛况

          开班时间:2021-05-17(北京)

          开班盛况
        • 大数据+人工智能 <高端班>

          开班时间:2021-03-22(杭州)

          开班盛况

          开班时间:2021-04-26(北京)

          开班盛况
        • JavaEE分布式开发 <高端班>

          开班时间:2021-05-10(北京)

          开班盛况

          开班时间:2021-02-22(北京)

          开班盛况
        • Python人工智能+数据分析 <高端班>

          开班时间:2021-07-12(北京)

          预约报名

          开班时间:2020-09-21(上海)

          开班盛况
        • 云计算开发 <高端班>

          开班时间:2021-07-12(北京)

          预约报名

          开班时间:2019-07-22(北京)

          开班盛况
        IT培训IT培训
        在线咨询
        IT培训IT培训
        试听
        IT培训IT培训
        入学教程
        IT培训IT培训
        立即报名
        IT培训

        Copyright 2011-2023 北京千锋互联科技有限公司 .All Right 京ICP备12003911号-5 京公网安备 11010802035720号

        国产三级片在线视频
        <strong id="8cmf8"><pre id="8cmf8"></pre></strong><progress id="8cmf8"><track id="8cmf8"></track></progress>

        <button id="8cmf8"><object id="8cmf8"></object></button>

          <button id="8cmf8"><acronym id="8cmf8"></acronym></button>

        1. <button id="8cmf8"><acronym id="8cmf8"></acronym></button>