• java泛型——基本使用


    泛型

    因为泛型使用较多的场景是在集合中,我们以集合为例来说说泛型。java集合的一个缺点就是,我们放入一个东西之后,他并不知道这个东西的数据类型。如何理解?看下面的代码。

            List list=new ArrayList();
            list.add("Sherry");
            list.add(18);

    这段代码编译、运行是没有任何问题的,但会报警告(下面会介绍)。现在将两个元素放入集合list中,当往外读的时候就有问题了,因为集合并不知道哪个对象具体是什么数据类型的。

    当我们从集合中读数据时,因为不知道具体的数据类型,经常会遇到类型转换的问题,比如:

            //输出
            for (int i = 0; i < list.size(); i++) {
                System.out.println((String)list.get(i));
            }

    报错信息:

    Exception in thread “main” java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
    at com.ysx.test.GenericTest.main(GenericTest.java:16)


    如何解决

    这里写图片描述

    上图将不同类型的元素放入各自的集合中,这时我们进行读写操作时就不需要考虑是否需要类型转换,如何转,是否可以转换成功等问题了。

    解决的思路很简单,就是将类型抽象出来作为参数。在定义集合的时候就说明是用来存放何种类型的。

    将上面的例子改造下,改造之后代码就不报警告了。

            List<String> listStr=new ArrayList<String>();
            listStr.add("Sherry");
            listStr.add("123");
            for (int i = 0; i < listStr.size(); i++) {
                System.out.println(listStr.get(i));
            }

    Tips:

    因为List<String> listStr=new ArrayList<String>();中ArrayList后面的String显得有些多余,java7之后就不需要写完整信息了。写成List<String> listStr=new ArrayList<>();就可以了。

    书上的定义:

    泛型,就是允许在定义类、接口、方法时使用类型形参,这个形参将在声明变量、创建对象、调用方法时动态的指定。

    回顾下List的接口定义:

    public interface List<E> extends Collection<E> {
    }

    在定义接口的时候指定了类型形参E,因此,我们在使用的时候才可以直接在List后面加参数类型。
    Collection和Map可以说是集合的父接口,其他都是对他们的继承或者实现,我们可以说java集合对支持泛型提供了良好的基础。

    当然,我们可以不需要把泛型加在类或接口上,而是直接加到某个方法上,即泛型方法。

    泛型方法

    先看个例子:

    public class GenericMethodTest {
        //泛型方法
        public static <T> void Test(List<T> c) {
            for (int i = 0; i < c.size(); i++) {
                System.out.println(c.get(i));
            }
        }
    
        public static void main(String[] args) {
            // List<Object>
            List<Object> obj = new ArrayList<>();
            obj.add(2);
            obj.add("dd");
            Test(obj);
    
            // List<Stromg>
            List<String> strList = new ArrayList<>();
            strList.add("a");
            strList.add("b");
            Test(strList);
        }
    }
    

    在该泛型方法中,使用了T类型形参,他的位置应该在修饰符(public、static、final等)之后,在返回值之前。

    泛型方法和泛型类区别归根到底是方法和类,比如:作用域等。

    泛型方法还有一个好朋友——类型通配符,他们经常被拿来作比较。

    类型通配符

    当我们使用一个泛型类或方法时,如果不传入实际类型参数,就会给出黄色警告。
    比如:

    public void test(List c){
        for(int i=0;i<c.size();i++){
            System.out.println(c.get(i));
        }
    }

    在例子中,List c 并没有指定具体的类型,这个集合中需要放入的类型并不确定,我们无法加上任意一种形式。这时,使用通配符“?”就可以避免这个问题。

    修改一下代码:

    public void test(List<?> c){
        for(int i=0;i<c.size();i++){
            System.out.println(c.get(i));
        }
    }

    使用类型通配符之后,警告就不见了。但因为集合中的类型是不确定的,我们只可以进行简单的查询操作,没办法添加元素,除了添加null情况。

    有人会问了,那这通配符有什么用啊,集合的常用操作都实现不了,加不加有什么用啊?别急,他的精髓还在后面……

    设定上限

    List<?>代表元素类型未知的List集合,他的元素可以匹配任何类型。更多时候,我们希望他只匹配某一类泛型的父类。

    我们以MiniCat-》Cat-》Animal为例,三者之间是继承关系。

       public static void test3(List<? extends Cat> cats) {
            for (Cat c : cats) {
                System.out.println(c.getName());
            }
        }

    下面我们开始调用,上面例子中的意思是Cat类以及其子类可以放入List集合中,我们来试一下他的父类Animal。

    public static void main(String[] args) {
    
            List<Animal> a = new ArrayList<Animal>();
            a.add(new Animal("pig", "white"));
            a.add(new Animal("apple", "white"));
            test3(a);
    
        }

    此时,编译报错,报错信息为:

    这里写图片描述

    之后,将Animal改为Cat类,顺利执行。

    下限

    下限的设定使用<? super Class>

    public static void test4(List<? super Cat> cats) {
            for (Object obj: cats) {
                Cat c = (Cat) obj;
                System.out.println(c.getName());
            }
        }

    这代表下限是Cat类,满足条件的是Cat的父类,这时如果传入MiniCat类,仍旧会报上面的错。


    比较

    大多数情况下都可以使用泛型方法来代替类型通配符。

    通配符支持灵活的子类化。

    若方法中的参数或返回值之间有类型依赖关系,应该使用泛型方法。

    有时候,他们两者也结合使用。

  • 相关阅读:
    Cryptography中的对称密钥加解密:fernet算法探究
    HTTPS的工作原理
    最近要写的博客
    浅谈路由器软硬件架构
    组管理、权限管理、定时任务调度、磁盘分区
    matplotlib数据可视化
    tensorflow实现简单的卷积神经网络
    tensorflow实现简单的感知机
    tensorflow实现简单的自编码器
    区域生长算法(手动选取种子点)MATLAB
  • 原文地址:https://www.cnblogs.com/saixing/p/6730217.html
Copyright © 2020-2023  润新知