前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Java 泛型(Generic)

Java 泛型(Generic)

原创
作者头像
HLee
修改于 2021-09-13 02:08:24
修改于 2021-09-13 02:08:24
94100
代码可运行
举报
文章被收录于专栏:房东的猫房东的猫
运行总次数:0
代码可运行

简介

Java在1.5之后加入了泛型的概念。泛型,即“参数化类型”。泛型的本质是为了参数化类型(将类型参数化传递)(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

Java泛型有三种使用方式:泛型类、泛型方法、泛型接口。

泛型信息只存在于代码编译阶段,在进入JVM之前,与泛型相关的信息会被擦除掉,我们称之为“泛型擦除”。

泛型类

泛型类的语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
修饰符 class 类名称 <泛型类型标识>{

    /*(成员变量类型)*/
    private 泛型标识  var; 
}

备注:常用的泛型标识:TEKV

我们首先定义一个简单的类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * @describtion 泛型类定义
 * 此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
 * 在实例化泛型类时,必须指定T的具体类型
 * @param <T> 泛型标识-类型参数
 */
public class Generic<T> {

    /**
     * T 由外部使用类的时候来指定
     */
    private T t;

    public Generic(T key) {
        this.t = t;
    }

    public T getT() {
        return t;
    }

    public void setT(T t){
        this.t = t;
    }
}


//传入的实参类型需与泛型的类型参数类型相同,即为Integer.
Generic<Integer> genericInteger = new Generic<Integer>(123456);
//传入的实参类型需与泛型的类型参数类型相同,即为String.
Generic<String> genericString = new Generic<String>("key_vlaue");

备注:

  • 泛型类的类型参数只能是类类型,不支持基本类型 ;
  • 同一泛型,根据不同的数据类型创建的对象,本质上是同一类型
  • 泛型类,如果没有指定具体的数据类型,此时,操作类型是Object;

子类也是泛型类,子类与父类的泛型类型要一致

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ChildGeneric<T> extends Generic<T>
class ChildGeneric<T, K, V> extends Generic<T>

子类不是泛型类,父类要明确泛型的数据类型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class ChildGeneric extends Generic<String>

泛型接口

泛型接口与泛型类的定义及使用基本相同。泛型接口常被用在各种类的生产器中,可以看一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 定义一个泛型接口
public interface Generator<T> {
    T getKey();
}

当实现泛型接口的类,未传入泛型实参时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 泛型接口的实现类是一个泛型类,那么要保证实现接口的泛型类的泛型标识包含接口的泛型标识
 */
public class Pair<T, E> implements Generator<T>{

    private T key;
    private E value;

    public Pair(T key, E value) {
        this.key = key;
        this.value = value;
    }

    @Override
    public T getKey() {
        return key;
    }

    public E getValue() {
        return value;
    }
}

当实现泛型接口的类,传入泛型实参时:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 实现泛型接口的类不是泛型类,需要明确实现泛型接口的数据类型
 */
public class Apple implements Generator<String> {

    @Override
    public String getKey() {
        return "hello Generator";
    }
}

泛型方法

泛型方法的语法如下: 其中<泛型类型标识>必须存在。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[作用域修饰符] <泛型类型标识> [返回类型] 方法名称(参数列表){}

* 泛型方法能使方法独立于类而产生变化
* 如果static方法要使用泛型能力,就必须使其成为泛型方法
  • 泛型类,是在实例化类的时候指明泛型的具体类型;
  • 泛型方法,是在调用方法的时候指明泛型的具体类型。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * 泛型方法的基本介绍
 * @param tClass 传入的泛型实参
 * @return T 返回值为T类型
 * 说明:
 *     1)public 与 返回值中间<T>非常重要,可以理解为声明此方法为泛型方法。
 *     2)只有声明了<T>的方法才是泛型方法,泛型类中的使用了泛型的成员方法并不是泛型方法。
 *     3)<T>表明该方法将使用泛型类型T,此时才可以在方法中使用泛型类型T。
 *     4)与泛型类的定义一样,此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型。
 */
public <T> T genericMethod(Class<T> tClass)throws InstantiationException ,
  IllegalAccessException{
        T instance = tClass.newInstance();
        return instance;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class GenericMethod {

    // 这个类是个泛型类
    public class Generic<T>{

        private T key;

        public Generic(T key) {
            this.key = key;
        }

        // 虽然在方法中使用了泛型,但是这并不是一个泛型方法。这只是类中一个普通的成员方法,只不过他的返回值是在声明泛型类已经声明过的泛型。
        // 所以在这个方法中才可以继续使用 T 这个泛型。
        public T getKey(){
            return key;
        }

         // 这个方法显然是有问题的,在编译器会给我们提示这样的错误信息"cannot reslove symbol E"
         // 因为在类的声明中并未声明泛型E,所以在使用E做形参和返回值类型时,编译器会无法识别。
         // public E setKey(E key){this.key = keu}
    }

    /**
     * 泛型方法。
     * 首先在public与返回值之间的<T>必不可少,这表明这是一个泛型方法,并且声明了一个泛型T
     * 这个T可以出现在这个泛型方法的任意位置.
     * 泛型的数量也可以为任意多个
     * 如:public <T,K> K showKeyName(Generic<T> container){}
     */
    public <T> T showKeyName(Generic<T> container){
        System.out.println("container key :" + container.getKey());
        //当然这个例子举的不太合适,只是为了说明泛型方法的特性。
        T test = container.getKey();
        return test;
    }

    // 这也不是一个泛型方法,这就是一个普通的方法,只是使用了Generic<Number>这个泛型类做形参而已。
    public void showKeyValue1(Generic<Number> obj){
        System.out.println("泛型测试: key value is " + obj.getKey());
    }

    // 这也不是一个泛型方法,这也是一个普通的方法,只不过使用了泛型通配符?
    // 同时这也印证了泛型通配符章节所描述的,?是一种类型实参,可以看做为Number等所有类的父类
    public void showKeyValue2(Generic<?> obj){
        System.out.println("泛型测试: key value is " + obj.getKey());
    }

     // 这个方法是有问题的,编译器会为我们提示错误信息:"UnKnown class 'E' "
     // 虽然我们声明了<T>,也表明了这是一个可以处理泛型的类型的泛型方法。
     // 但是只声明了泛型类型T,并未声明泛型类型E,因此编译器并不知道该如何处理E这个类型。
     // public <T> T showKeyName(Generic<E> container){}
     
       
     // 这个方法也是有问题的,编译器会为我们提示错误信息:"UnKnown class 'T' "
     // 对于编译器来说T这个类型并未项目中声明过,因此编译也不知道该如何编译这个类。
     // 所以这也不是一个正确的泛型方法声明。
     // public void showkey(T genericObj){}
}

类中泛型方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class GenericFruit {

    class Fruit{
        @Override
        public String toString() {
            return "fruit";
        }
    }

    class Apple extends Fruit{
        @Override
        public String toString() {
            return "apple";
        }
    }

    class Person{
        @Override
        public String toString() {
            return "Person";
        }
    }

    static class GenerateTest<T>{
        
        public void show_1(T t){
            System.out.println(t.toString());
        }

        // 在泛型类中声明了一个泛型方法,使用泛型E,这种泛型E可以为任意类型。可以类型与T相同,也可以不同。
        // 由于泛型方法在声明的时候会声明泛型<E>,因此即使在泛型类中并未声明泛型,编译器也能够正确识别泛型方法中识别的泛型。
        public <E> void show_3(E t){
            System.out.println(t.toString());
        }

        // 在泛型类中声明了一个泛型方法,使用泛型T,注意这个T是一种全新的类型,可以与泛型类中声明的T不是同一种类型。
        public <T> void show_2(T t){
            System.out.println(t.toString());
        }
    }

    public static void main(String[] args) {
        
        Apple apple = new GenericFruit().new Apple();
        Person person = new GenericFruit().new Person();

        GenerateTest<Fruit> generateTest = new GenerateTest<Fruit>();
        
        // apple是Fruit的子类,所以这里可以
        generateTest.show_1(apple);
        
        // 编译器会报错,因为泛型类型实参指定的是Fruit,而传入的实参类是Person
        // generateTest.show_1(person);

        // 使用这两个方法都可以成功
        generateTest.show_2(apple);
        generateTest.show_2(person);

        // 使用这两个方法也都可以成功
        generateTest.show_3(apple);
        generateTest.show_3(person);
    }
}

泛型可变参数方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public <T> void printMsg( T... args){
    for(T t : args){
        System.out.println("泛型测试: t is " + t);
    }
}

printMsg("111", 222, "aaaa", "2323.4", 55.55);

泛型静态方法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class StaticGenerator<T> {

    /**
     * 如果在类中定义使用泛型的静态方法,需要添加额外的泛型声明(将这个方法定义成泛型方法)
     * 即使静态方法要使用泛型类中已经声明过的泛型也不可以。
     * 如:public static void show(T t){..},此时编译器会提示错误信息:
     *    "StaticGenerator cannot be refrenced from static context"
     */
    public static <T> void show(T t){}
}

如果静态方法要使用泛型的话,必须将静态方法也定义成泛型方法。

泛型通配符

类型通配符一般是使用?代替具体的类型实参,注意了,此处’?’是类型实参,而不是类型形参。

  • 类型通配符一般使用?代替具体的类型实参
  • 类型通配符是类型实参,而不是类型形参
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Animal {}

public class Cat extends Animal{}

public class MiniCat extends Cat {}

类型通配符上限

要求该泛型的类型,只能是实参类型,或实参类型的子类类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
语法:类/接口 <? extends 实参类型>

public static void main(String[] args) {

    ArrayList<Animal> animals = new ArrayList<>();
    ArrayList<Cat> cats = new ArrayList<>();
    ArrayList<MiniCat> miniCats = new ArrayList<>();

    // showAnimal_up(animals); // 不符合泛型上限通配符要求,只能是实参类型,或实参类型的子类类型
    showAnimal_up(cats);
    showAnimal_up(miniCats);
}

/**
 * 类型通配符上限:要求该泛型的类型,只能是实参类型,或实参类型的子类类型
 * @param list
 */
public static void showAnimal_up(ArrayList<? extends Animal> list) {
    
    // list.add(new Cat()); // 不允许添加
    list.forEach(v -> {
        System.out.println(v);
    });
}

类型通配符下限

要求该泛型的类型,只能是实参类型,或实参类型的父类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
语法:类/接口 <? super 实参类型>

public static void main(String[] args) {

    ArrayList<Animal> animals = new ArrayList<>();
    ArrayList<Cat> cats = new ArrayList<>();
    ArrayList<MiniCat> miniCats = new ArrayList<>();

    showAnimal_down(animals);
    // showAnimal_down(cats);
    // showAnimal_down(miniCats);
}

/**
 * 类型通配符下限:要求该泛型的类型,只能是实参类型,或实参类型的父类
 * @param list
 */
public static void showAnimal_down(ArrayList<? super Animal> list) {
    
    list.add(new Cat()); // 允许添加
    list.forEach(v -> {
        System.out.println(v);
    });
}

泛型数组

  • 可以声明带泛型的数组引用,但是不能直接创建带泛型的数组对象
  • 可以通过java.lang.reflect.Array的newInstance(Class<T>, int)创建T[]数组
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Fruit<T> {

    private T[] array;

    // 构造函数
    public Fruit(Class<T> clz, int length) {
        // 通过Array.newInstance创建泛型数组
        array = (T[])Array.newInstance(clz, length);
    }

    /**
     * 填充数组
     */
    public void putArray(int index, T item) {
        array[index] = item;
    }

    /**
     * 获取数组
     */
    public T getArray(int index) {
        return array[index];
    }
}

Fruit<String> fruit = new Fruit<>(String.class, 3);
fruit.putArray(0, "橘子");
fruit.putArray(1, "香蕉");
fruit.putArray(2, "苹果");

System.out.println(fruit.getArray(0));

不能创建一个确切的泛型类型的数组,例如:
List<String>[] ls = new ArrayList<String>[10];  

使用通配符创建泛型数组,例如:
List<?>[] ls = new ArrayList<?>[10];  
List<String>[] ls = new ArrayList[10];

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
五分钟实现pdf分页
抱歉也开始用了这么“标题党”的标题。事情起源于前几天需要把个人资料的pdf文档一页一页的拆出来,好传到相关的网站上。直接截图到word再转pdf比较麻烦,所以想用工具直接转换。结果找了几个pdf阅读器,这类操作都需要会员或收费。作为一名程序员,这么简单的操作还要收费显然是一种羞耻(当然我是不会承认主要是因为qiong的),几分钟就可以代码解决的问题为啥要花钱呢?废话不多说,开搞。
程序员架构进阶
2023/02/28
2.1K0
五分钟实现pdf分页
Java一个工具类实现PDF加水印
最近公司做一个需求,给用户上传的文件word和pdf文件进行添加水印,若是doc、docx文件先进行word转pdf然后加水印,如果是pdf文件直接进行加水印。
Lcry
2023/03/15
3.6K0
Java一个工具类实现PDF加水印
使用 Apache PDFBox 操作PDF文件
Apache PDFBox库是一个用于处理PDF文档的开源Java工具。该项目允许创建新的PDF文档,操作现有PDF文档,并从PDF文档中提取内容。Apache PDFBox还包括几个命令行实用程序。
万猫学社
2023/09/01
2.9K0
使用 Apache PDFBox 操作PDF文件
SpringBoot 下PDF生成使用填坑总结
解释: 1、Configuration cfg 使用了freemaker starter后,在项目启动时即会自动初始化 Configuration 对象到Spring容器中; 2、Template template = cfg.getTemplate("test.ftl","UTF-8"); 模板因cfg本身在Spring容器中,则在获取test.ftl模板是就会自动在resource/templates下寻找模板,默认:ftl 格式,可以修改 3、因为找了很多例子都是使用ITextRenderer 对象来渲染输出渲染的PDF,但ITextRenderer有一个问题是要解决中文不显示问题,必须把字体放在一个以 文件夹 路径访问的形式引入,SpringBoot打包后,经测试,无法获取打包后的FONT字体; 则,再另辟途径,又找到以Document方式,但document需要的是,没一个dom对象都必须一个个添加进去,网上很多都是new 专门的对象,比如:块 Paragraph 然后添加文字(数字)内容。 所以又搜索:是否可以往document插入html 最终找到:https://www.cnblogs.com/mvilplss/p/5646675.html
林淮川
2021/12/20
5.2K0
SpringBoot 下PDF生成使用填坑总结
Java 渲染 docx 文件,并生成 pdf 加水印
一顿google以后发现了 StackOverflow 上的这个回答:Converting docx into pdf in java 使用如下的 jar 包:
用户2060079
2018/10/11
3.2K0
PDF转Word完全指南:3大方法满足各种场景!
还不知道PDF怎么转Word吗,本文将提供完整的PDF转Word方案,包括离线、在线或者SDK API等各种方式,总有一款满足您的需求。
用户10034170
2022/12/07
4.7K0
PDF转Word完全指南:3大方法满足各种场景!
PDF文件添加二维码水印教程
maven配置iText的jar,主要不是所有私服都有iText的jar,maven仓库没有的,可以去https://mvnrepository.com/artifact/com.itextpdf/itextpdf/5.5.12 这里下载
SmileNicky
2019/06/15
1.6K0
iText5实现Java生成PDF文件完整版
最近项目中使用到Java实现导出PDF文件,经过一番参考研究最终决定使用itextpdf来实现,当然也可以参考 PDF Java类库:Spire.PDF for Java(https://www.e-iceblue.cn/spirepdfjava/create-pdf-in-java.html)。本文是使用第一种来实现的。
全栈程序员站长
2022/07/02
6.7K1
iText5实现Java生成PDF文件完整版
导出pdf文件时加图片水印
前言:本文探讨的是利用pageEvent事件在生成PDF文件的过程中把图片水印加好,这样导出的是加好水印的pdf文件。
全栈程序员站长
2022/08/26
1.5K0
导出pdf文件时加图片水印
AI文档智能助理都是如何处理pdf的?
在AI盛行的当下,基于文档的本地知识库智能问答系统已经成为当下最受AI从业者欢迎的落地方式。本文旨在收集整理当下AI应用中使用较多的处理pdf的库和开源项目,喜欢的请点赞、收藏。
山行AI
2023/08/10
1.4K0
AI文档智能助理都是如何处理pdf的?
Java系列:快速实现PDF转图片
🤞Java系列:快速实现PDF转图片🤞 PDFBox是一个BSD许可下的源码开放项目,它是一个专门为开发人员读取和创建PDF文档而准备的纯Java类库。 文本提取:PDFBox可以从PDF文档中提取文本,包括Unicode字符。 表单处理:PDFBox支持从PDF表单中提取数据,或者是填充PDF表单。 加密与解密:可以对PDF文档进行加密和解密操作。 合并与分割:可以将多个PDF文档合并成一个,也可以将一个PDF文档分割成多个。 覆盖与追加:可以覆盖PDF文档的内容,或者向已有的PDF文档中追加内容。 打印:利用Java的打印API,PDFBox可以将PDF文档输出到打印机。 转换:PDFBox可以将PDF文档转换成映像文件,如PNG或JPEG格式。 创建PDF:可以从头开始创建新的PDF文档,包括添加图像和字体。 集成Lucene搜索引擎:PDFBox与Jakarta Lucene等文本搜索引擎的整合过程十分简单,可以用于PDF文档的索引和搜索。
知识浅谈
2025/05/16
1790
Java系列:快速实现PDF转图片
Spring Boot集成PDFBox实现电子签章
随着无纸化办公的普及,电子文档的使用越来越广泛。电子签章作为一种有效的身份验证方式,在很多场景下替代了传统的纸质文件签名。Apache PDFBox 是一个开源的Java库,可以用来渲染、生成、填写PDF文档等操作。本文将介绍如何使用Spring Boot框架结合PDFBox来实现电子签章功能。
程序员皮皮林
2024/10/12
4160
多种格式文件合并为pdf(大数据量)
最近遇到了一个比较恶心的需求,就是用户为了安全性,本地的富文本文件不存放到我们的minio中,而是富文本的原件存在了客户的服务器中,但是我们只能在数据库存放对应的路径。 富文本与档案的绑定关系是多对一,也就是多个富文本绑定一个档案,现在有个问题就是我们系统都是档案都是以pdf进行展示的,而客户那边档案下对应的富文本是多种数据类型的包括以下几种:jpg,png,jpeg,pdf,tif,tiff 所以我要去做兼容也就是说需要把档案下对应的各种格式的富文本拼接成一个pdf,然后再去展示。
用户10136162
2023/10/30
3370
【iText5 生成PDF】纯Java代码实现生成PDF(自定义表格、文本水印、单元格样式)
​ 工作中遇到需要生成PDF。最终选择了iText。其他也有通过html再生成。感觉不太适合就用了代码实现。 使用iText 5.5.13.1版本。纯Java代码实现 1.自定义表格合并指定行列
小帅丶
2019/07/22
8.9K0
【iText5 生成PDF】纯Java代码实现生成PDF(自定义表格、文本水印、单元格样式)
Java 解析pdf文档内容实战案例
上面这个文件是相对比较规整格式的文件,实际情况应该会有许多报表格式不一样,解析出来的有换行之类的,需要特殊处理。
跟着飞哥学编程
2022/11/30
2.3K0
Java 解析pdf文档内容实战案例
Java实现pdf转html
引入pdf2dom <dependency> <groupId>net.sf.cssbox</groupId> <artifactId>pdf2dom</artifactId> <version>1.8</version> </dependency> 测试代码: import java.io.File; import java.io.FileInputStream; import java.io.IOEx
JadePeng
2022/05/11
3.9K0
java 利用 pdfbox 实现PDF转为图片
当时放到测试环境后一直显示乱码,看了下报错是说字体不存在。第一个想法是在主机上安装字体,但是又有问题了,生产不可能这样吧,运维也不同意啊。
Li_XiaoJin
2022/06/10
4.1K0
java 利用 pdfbox 实现PDF转为图片
Spring Boot 中集成 iText 实现基于 PDF 模板的内容替换
🤞Spring Boot 中集成 iText 实现基于 PDF 模板的内容替换🤞
知识浅谈
2024/05/25
1.4K0
aspose word转换pdf文件后将pdf文件转换为图片png
链接:aspose word模板文件生成pdf https://www.cnblogs.com/oktokeep/p/16615900.html
oktokeep
2024/10/09
4300
Java输出Pdf(2021)集成springboot
1.首先,新建一个word文档,内容如下,另存为pdf格式,我的命名:mytest.pdf。
知识浅谈
2021/05/10
1.1K0
Java输出Pdf(2021)集成springboot
推荐阅读
相关推荐
五分钟实现pdf分页
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档