专治Java底子差,不要再认为泛型就是一对尖括号了

Posted 緑水長流*z

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了专治Java底子差,不要再认为泛型就是一对尖括号了相关的知识,希望对你有一定的参考价值。

💗推荐阅读文章💗

🎉本博客知识点收录于🎉👉🚀《JavaSE系列教程》🚀—>✈️11【泛型、Map、异常】✈️

文章目录

一、泛型

1.1 泛型概述

泛型定义:把类型明确的工作延迟到创建对象或调用方法的时候才去明确的特殊的类型;

例如,我们知道集合是可以存储任意元素的,那么这样一想,add方法上的参数应该是Object(所有类的父类),但是这样会引入一个新的问题,我们知道,子类都是比父类强大的,我们在使用的时候肯定是希望获取的是当初存进去的具体子类对象;因此我们每次都需要进行强制转换;

但add方法真的是Object吗?

查看ArrayList的add方法:

class ArrayList<E> 
    public boolean add(E e) 

    public E get(int index) 
   	....

Collection类:

public interface Collection<E> extends Iterable<E> 

上面的E就是泛型,集合的定义者也不知道我们需要存储什么元素到集合中,具体的类型只能延迟到创建对象时来决定了

1.2 集合泛型的使用

1.2.1 未使用泛型

定义一个Province对象:

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Province 

    private String name;                        // 名称
    private String shortName;                    // 简称
    private String location;                    // 所属区域

    public void intro() 
        System.out.println("名称: " + name + ",简称: " + shortName + ",所属地区: " + location);
    

    @Override
    public String toString() 
        return "Province" +
                "name='" + name + '\\'' +
                ", shortName='" + shortName + '\\'' +
                ", location='" + location + '\\'' +
                '';
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getShortName() 
        return shortName;
    

    public void setShortName(String shortName) 
        this.shortName = shortName;
    

    public String getLocation() 
        return location;
    

    public void setLocation(String location) 
        this.location = location;
    

    public Province() 
    

    public Province(String name, String shortName, String location) 
        this.name = name;
        this.shortName = shortName;
        this.location = location;
    

测试类:

package com.dfbz.demo01;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo01_泛型问题引出 
    public static void main(String[] args) 

        Collection list = new ArrayList();

        // 往集合存储对象
        list.add(new Province("台湾", "台","华东"));
        list.add(new Province("澳门", "澳","华南"));
        list.add(new Province("香港", "港","华南"));
        list.add(new Province("河北", "冀","华北"));

        // 获取迭代器
        Iterator iterator = list.iterator();

        while (iterator.hasNext()) 

            // 集合中的元素都被提升为Object对象了
            Object obj = iterator.next();

            // 强制转换为子类
            Province province = (Province) obj;

            // 调用子类特有的功能
            province.intro();
        
    

我们没有给泛型进行明确的定义,对象存储到集合中都被提升为Object类型了,取出来时需要强制转换为具体子类,非常麻烦;

不仅如此,这样的代码还存在这隐藏的风险,集合中可以存储任意的对象,如果往集合中存储其他对象呢?

定义个Book对象:

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Book 
    private String name;
    private String author;

    public void detail() 
        System.out.println("书名: " + name + ",作者: " + author);
    

    @Override
    public String toString() 
        return "Book" +
                "name='" + name + '\\'' +
                ", author='" + author + '\\'' +
                '';
    

    public String getName() 
        return name;
    

    public void setName(String name) 
        this.name = name;
    

    public String getAuthor() 
        return author;
    

    public void setAuthor(String author) 
        this.author = author;
    

    public Book() 
    

    public Book(String name, String author) 
        this.name = name;
        this.author = author;
    

测试类:

package com.dfbz.demo01;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo02_泛型出现的问题 
    public static void main(String[] args) 
        Collection list = new ArrayList();

        // 添加几个省份
        list.add(new Province("山西", "晋","华北"));
        list.add(new Province("河南", "豫","华中"));
        list.add(new Province("江西", "赣","华东"));

        // 添加几本书
        list.add(new Book("《史记》","司马迁"));
        list.add(new Book("《三国志》","陈寿"));

        // 获取迭代器
        Iterator iterator = list.iterator();

        while (iterator.hasNext()) 

            // 集合中的元素都被提升为Object对象了
            Object obj = iterator.next();

            // 强制转换为子类
            Province province = (Province) obj;         // 不可以将Book转换为Province(存在隐藏问题)

            // 调用子类特有的功能
            province.intro();
        
    

运行结果:

上述代码编译时是没有什么问题,但运行时出现了类型转换异常:ClassCastException,代码存在一定的安全隐患;

1.2.2 使用泛型

  • 查看List源码:
public interface List<E> extends Collection<E> 
    boolean add(E e);
   	....

集合的定义者发现,无法在定义集合类时就确定该集合存储的具体类型,因此使用泛型进行占位,使用者创建集合时明确该泛型的类型;

  • 指定泛型后:
class List<Province> 
    boolean add(Province province);
   	....

Tips:在创建对象时指定泛型的类型,泛型一旦指定了具体的类型,原来泛型的占位符(E),都将变为此类型;

如何指定泛型类类型?

格式如下:

List<泛型的具体类型> list=new ArrayList();

测试类:

package com.dfbz.demo01;

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

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo03_使用泛型来存储元素 
    public static void main(String[] args) 

        // 创建集合,并确定好泛型(存储的类型)
        List<Province> list = new ArrayList();

        // 往集合存储对象
        list.add(new Province("甘肃", "甘|陇","西北"));
        list.add(new Province("陕西", "陕|秦","西北"));
        list.add(new Province("贵州", "贵|黔","西南"));
        list.add(new Province("云南", "云|滇","西南"));
        list.add(new Province("四川", "川|蜀","西南"));

        // 获取City类型的迭代器
        Iterator<Province> iterator = list.iterator();

        while (iterator.hasNext())

            // 直接获取province类型
            Province province = iterator.next();

            province.intro();
        
    

1.3 泛型类

1.3.1 泛型类的使用

很明显,Collection、List、Set以及其下的子类都是泛型类,我们根据使用情况也可以定义泛型类;让泛型类的类型延迟到创建对象的时候指定;

  • 使用格式:
修饰符 class 类名<代表泛型的变量> 

例如,API中的List接口:

class List<E> 
    boolean add(E e);
   	....

在创建对象的时候确定泛型

例如,List<String> list = new ArrayList<String>();

此时,变量E的值就是String类型,那么我们的类型就可以理解为:

public interface List<String> extends Collection<String> 
     boolean add(String e);
     ...

再例如,ArrayList<Integer> list = new ArrayList<Integer>();

此时,变量E的值就是Integer类型,那么我们的类型就可以理解为:

public interface List<Integer> extends Collection<Integer> 
     boolean add(Integer e);
     ...

举例自定义泛型类:

package com.dfbz.demo01;

public class GetClass<P> 

    // 使用泛型类型,P具体的类型还不明确,等到创建GetClass对象时再明确P的类型
    private P p;

    public P getC() 
        return p;
    

    public void setC(P p) 
        this.p = p;
    

使用:

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo04_自定义泛型类 
    public static void main(String[] args) 
        // 创建对象,并明确泛型类型为City
        GetClass<Province> getClass = new GetClass<>();

        // setC(Province province)
        getClass.setC(new Province("新疆","新","西北"));

        // Province getC()
        Province province = getClass.getC();

        province.intro();
    

1.2.2 泛型类的继承

定义一个父类,带有泛型:

public class 类名<泛型类型>   

例如,

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Fu<E> 
    public void show(E e) 
        System.out.println(e.toString());
    

使用格式:

  • 1)定义类时确定泛型的类型

例如

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro: 在定义子类时就确定好泛型的类型
 */
public class Zi1 extends Fu<Province> 

    @Override
    public void show(Province province) 
        System.out.println("zi1: " + province);
    

此时,泛型E的值就是Province类型。

  • 2)始终不确定泛型的类型,直到创建对象时,确定泛型的类型

例如

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro: 子类也变为泛型类,泛型类型具体到创建对象的时候再确定
 */
public class Zi2<P> extends Fu<P> 

    @Override
    public void show(P p) 
        System.out.println("zi2: " + p);
    

确定泛型:

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo05_测试泛型类的继承 
    public static void main(String[] args) 

        // 注意: Zi1类以及不是一个泛型类了,不能够指定泛型
//        Zi1<Book> zi1=new Zi1();          // 报错

        // 创建子类时确定泛型类型
        Zi2<Province> zi_a = new Zi2<>();
        zi_a.show(new Province("青海", "青", "西北"));


        Zi2<Book> zi_b = new Zi2<>();
        zi_b.show(new Book("《说岳全传》", "钱彩"));
    

1.4 泛型方法

泛型类是在创建类时定义泛型类型,在创建对象时确定泛型类型;泛型方法则是在创建方法是定义泛型类型,在调用方法时确定泛型类型;

  • 定义格式:
修饰符 <代表泛型的变量> 返回值类型 方法名(参数)

例如:

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class GetClassMethod 
    // 定义泛型方法
    public <P> P GetClass(P p) 
        return p;
    

使用格式:调用方法时,确定泛型的类型

package com.dfbz.demo01;

/**
 * @author lscl
 * @version 1.0
 * @intro:
 */
public class Demo06_测试泛型方法 
    public static void main(String[] args) 
        GetClassMethod getClassMethod = new GetClassMethod();

        // Province getClas

以上是关于专治Java底子差,不要再认为泛型就是一对尖括号了的主要内容,如果未能解决你的问题,请参考以下文章

JAVA泛型实现原理

《Effective Java》第5章 泛型

《疯狂Java讲义》(二十七)----泛型

java泛型 通配符

Java编程手册-泛型

泛型集合之ArrayList