Java泛型

一.泛型的概念

Java泛型(Generics)是JDK5引入的重要特性,它允许在定义类、接口、方法时使用类型参数,从而实现代码的类型安全和复用(使代码能够适用于多种数据类型)

泛型的机制可以让程序员在编译的时候就检测到非法的类型数据结构(与泛型指定的类型不一样,在编译阶段就会报错)

泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数

二.泛型产生的背景

java 复制代码
package fanxing;

import java.util.ArrayList;

// JDK5以前的写法:没有泛型
public class Background {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add("java");
        list.add(100);
        list.add(true);

        for (int i = 0; i < list.size(); i++) {
            Object o = list.get(i);// ctrl alt v 自动生成变量,变量的类型自动会写为Object
            //System.out.println(o); // 这样直接输出是没有什么问题
            // 但是有时候我们可能因为某些需求需要转换类型,就会发生类型转换错误
            // 比如:
            String s = (String)o;
            System.out.println(s); // ClassCastException
        }
    }
}

Java推出泛型以前,程序员可以构建一个元素为Object类型的集合,该集合可以存储任意类型的对象。而在适用集合的过程中,需要程序员明确知道存储的元素的类型,否则很容易引发ClassCastException异常

java 复制代码
package fanxing;

import java.util.ArrayList;

// JDK5及以后版本的写法:可以使用泛型
public class Background {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储String对象
        list.add("java");
        list.add("100");
        list.add("true");

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

成功输出,没有报错

java 复制代码
public class Background {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储Integer对象
        list.add(100);
        list.add(200);
        list.add(300);

        int sum = 0;
        for (int i = 0; i < list.size(); i++) {
            Integer num = list.get(i);
            sum += num; // JDK5以及以后的版本,都引入了自动装箱插箱机制
            // 自动装箱就是把基本类型的变量转换成对应的包装类对象,如int 转换成 Integer对象
            // 自动拆箱就是把对应的包装类对象转换成对应的基本类型变量,如Integer对象转换成int
            // 这里的加法就是发生了自动插箱机制
        }
        System.out.println(sum);
    }
}
java 复制代码
public class Background {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储Integer对象
        list.add(100);
        list.add(200);
        list.add(300);

        int sum = 0;
        for (int i = 0; i < list.size(); i++) {
            Integer num = list.get(i);
            //int intNum = (int) num; // 手动拆箱,Java规定包装类转基本类型时,调用对应的 xxxValue() 方法
            int intNum = num.intValue(); // 效果与上一行一样
            sum += intNum;
        }
        System.out.println(sum);
    }
}

三.泛型类

Java泛型使用特定的泛型标识符(类型参数)来代表未知的类型,虽然理论上可以用任意标识符,但社区形成了一套约定俗成的命名规范

Java官方推荐的类型参数标识符:

标识符 英文全称 含义 典型使用场景
T Type 类型 通用类型参数(最常用)
E Element 元素 集合类中的元素类型
K Key Map中的键类型
V Value Map中的值类型
N Number 数字 数值类型
R Return 返回值 方法的返回类型
S, U, V - 第二、三、四个类型 多个类型参数时依次使用
java 复制代码
package fanxing;

/**
 * 泛型类的定义
 * @param <T> 泛型标识,相当与类型参数,创建对象的时候会自动替换为具体的类型
 */
public class GenericClass<T> {
    private T value;

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

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public static void main(String[] args) {
        /*泛型类的使用方式:
        * 类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
        * Java1.7之后,后面的<>中的具体数据类型可以省略不写,如:
        * 类名<具体的数据类型> 对象名 = new 类名<>();*/

        // 在创建泛型类的对象时,要确定泛型类中具体的数据类型
        GenericClass<String> gc = new GenericClass<>("Java"); // 确定泛型类的数据类型为String,则传参就只能是String类型
        String value1 = gc.getValue(); // ctrl alt v自动生成变量声明,String对应了在创建对象的泛型参数
        System.out.println(value1);

        GenericClass gc2 = new GenericClass(123); // 如果在创建泛型类对象的时候没有指定具体的类型,则可以传递Object类型对象
        Object value2 = gc2.getValue();
        System.out.println(value2);

        //GenericClass<int> gc3 = new GenericClass<> (123); // 泛型参数不支持基本数据类型,需要使用包装类
    }
}

泛型类派生子类

1.若子类也是泛型类,则子类的泛型类型和父类的泛型类型要一致

java 复制代码
package fanxing;

public class Parent<E> {
    E e;
//
//    public Parent(E e) {
//        this.e = e;
//    }

    public E getE() {
        return e;
    }

    public void setE(E e) {
        this.e = e;
    }
}
java 复制代码
package fanxing;

public class ChildFirst<T> extends Parent<T> {
    @Override
    public T getE() {
        return super.getE();
    }
}
java 复制代码
package fanxing;

public class Test1 {
    public static void main(String[] args) {
        ChildFirst<String> cf = new ChildFirst<>();
        cf.setE("Hello World");
        String e = cf.getE();
        System.out.println(e); // Hello World
    }
}

2.若子类不是泛型类,则父类要明确泛型的数据类型

java 复制代码
package fanxing;

public class ChildSecond extends Parent<String>{
    @Override
    public String getE() {
        return super.getE();
    }
}
java 复制代码
package fanxing;

public class Test1 {
    public static void main(String[] args) {
        ChildFirst<String> cf = new ChildFirst<>();
        cf.setE("Hello World");
        String e = cf.getE();
        System.out.println(e); // Hello World

        ChildSecond cs = new ChildSecond();
        cs.setE("Hello Java");
        String e1 = cs.getE();
        System.out.println(e1); // Hello Java
    }
}

四.泛型接口

五.泛型方法

相关推荐
cyforkk2 小时前
YAML 基础语法与编写规范详解
java
Channing Lewis2 小时前
zoho crm的子表添加行时,有一个勾选字段,如何让它在details页面新建子表行(点击add row)时默认是勾选的
开发语言·前端·javascript
亓才孓2 小时前
[Spring测试]TestRestTemplate
java·后端·spring
逆光的July2 小时前
扫码登录的设计与实现
java
Miqiuha2 小时前
工作答辩框架
java·开发语言
happymaker06262 小时前
Java学习日记——DAY25(JavaSE完结)
java·开发语言·学习
CHANG_THE_WORLD2 小时前
C++指针与引用:从语法到底层的全面剖析
java·数据结构·c++
HAPPY酷2 小时前
Visual Studio C++ 项目“添加现有项“避坑指南
java·c++·visual studio
qq_24218863322 小时前
快速搭建跨环境检测服务的步骤
linux·开发语言·windows·python·macos