Java泛型的介绍和基本使用

news2024/9/20 19:57:01

什么是泛型

​ 泛型就是将类型参数化,比如定义了一个栈,你必须在定义之前声明这个栈中存放的数据的类型,是int也好是double或者其他的引用数据类型也好,定义好了之后这个栈就无法用来存放其他类型的数据。如果这时候我们想要使用这个栈来存放String(字符串类型)的数据就需要重写一遍代码,并且把数据类型改成String,可以说是代码重复性极高。

​ 类型参数化就可以很好的解决这个问题,我们在定义栈的时候,只需要给一个形参,在使用的时候将类型作为参数传递进去就可以使用这个栈来存放不同类型的数据了。

​ 泛型可以使用在接口,方法,和类里面,分别被称为泛型接口,泛型方法

java中的泛型标识符

下表是规范,用来增强代码的可读性,并非必须使用例如在下面的代码中使用了abc照样可以编译运行,不过可读性差,而且不规范。

下面的例子具体不用弄懂,下面会讲解,只需要知道abc和**?**在这里是通配符就好

public static <abc> void printList(List<? extends abc> list) {
  for (abc element : list) {
      System.out.println(element);
  }
}
标记符含义
EElement (在集合中使用,因为集合中存放的是元素)
TType(Java 类)
KKey(键)
VValue(值)
NNumber(数值类型)
可以表示任意类型(或者用来表示未知类型)

有限制的通配符

上限通配符-extends

这些所谓的上和下其实是描述在一个继承关系中的上下关系,对于一个类来说,它的父类的方向就是上,而子类的方向就是下。

所以这里的"上限"通配符的“上限”指的是,一个确定的父类,或者一个确定的接口来进行限制,该通配符表示的类型都是该父类的子类(确定的接口的实现类),或者子类的子类(继承关系在这个确定的父类以下的子类或者接口的实现类)

举个栗子
public class Add<T extends Number>{
    private T num;
    Add(T num){this.num = num;}
}

怎么使用呢?

正确调用
Add<Double> a = new Add<>(1.1);//正确
Add<Integer> i = new Add<>(3);//正确
错误调用
Add<String> s = new Add<>("1.1");//错误

通过这个例子我们应该就比较好理解了,Double和Integer都是Number类的子类,所以可以代替“T extends Number”中的T,而String不在Number的继承链中所以编译会报错。

在这里插入图片描述

虚线下面的类,确定的接口确定的父类就是上限通配符可以表示的类。

下限通配符-super

​ 下限通配符也是一种对泛型类型的限制,这里的“下限”指的是继承关系中的类型的下界。

再来一个栗子

这是一个泛型方法

    // 泛型方法接受 Number 及其父类的参数
    static void Generic(List<? super Number> list) {
        // 方法体
    }

方法调用

先看一个错误的示例

    List<Integer> list = new ArrayList<>();
    Generic(list);

下面都是正确的示例

List<Number> list = new ArrayList<>();
Generic(list);
List<Object> list = new ArrayList<>();
Generic(list);

​ OK从这个例子我们应该很明白啦,List<? super Number> list的尖括号中的数据类型必须是Number及其父类。

多重限定

用来限定类型,表示指定的类型参数必须是多个指定类或指定接口的子类。

public class GenericClass<T extends ClassA & InterfaceB & InterfaceC> {
    /**
    *方法和属性
    */
}
//表示T必须是ClassA的子类或者子类的子类并且实现了InterfaceB和InterfaceC接口,或者实现了接口的类的继承类

通过类型参数的限定,可以在泛型类或方法中对类型进行更精确的控制和约束,以提高代码的类型安全性和灵活性。。

好现在我们已经知道了泛型的常用的标识符了,现在我们来了解下泛型的三种应用

泛型类

单元泛型类

class Singl<T>{         // 这里的标识符可以随意命名,T是type的简称  
    private T var ;     
    public T getVar(){ 
        return var ;  
    }  
    public void setVar(T var){  
        this.var = var ;  
    }  
}  
public class Generics{  
    public static void main(String args[]){  
        Singl<String> p = new Singl<String>() ;     // 里面的var类型为String类型  
        p.setVar("it is love") ;                            // 设置字符串  
        System.out.println(p.getVar().length()) ;   // 取得字符串的长度  
    }  
}

多元泛型类

class Multal<K,V>{       // 这里指定了两个泛型类型 K和V 
    private K key ;    		//K表示Key,V表示Value
    private V value ;  
    public K getKey(){  
        return this.key ;  
    }  
    public V getValue(){  
        return this.value ;  
    }  
    public void setKey(K key){  
        this.key = key ;  
    }  
    public void setValue(V value){  
        this.value = value ;  
    }  
} 
public class GenericsClass{  
    public static void main(String args[]){  
        Multal<String,Integer> t  ;        // 定义两个泛型类型的对象  
        t = new Multal<String,Integer>() ;       // 里面的key为String,value为Integer  
        t.setKey("我爱罗") ;        // 设置第一个内容  
        t.setValue(20) ;            // 设置第二个内容  
        System.out.print("姓名;" + t.getKey()) ;      // 取得信息  
        System.out.print(",年龄;" + t.getValue()) ;       // 取得信息  
  
    }  
}

语法:定义泛型类

class:类名<泛型标识1,泛型标识2,泛型标识3.....>{	
	private 泛型标识1 变量名;
	private 泛型标识2 变量名;
}

从泛型类派生子类

派生子类有两种情况:

  • 子类(如果子类也是泛型类)与父类的类型保持一致(泛型标识保持一致),如果父类没有指明泛型类型,则按Object进行处理
  • 子类不是泛型类时,父类的泛型类型必须指明 例如,如果父类没有指明,则按照Object操作

子类也是泛型类

示例一:单一类型参数的泛型类

如果子类也是泛型类,子类的泛型标识必须和父类保持一致

// 定义一个泛型类
class GenericClass<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

// 从泛型类派生子类,子类也是泛型类
class SubGenericClass<U> extends GenericClass<U> {
    public SubGenericClass(U value) {
        super(value);
    }

    public void displayValue() {
        System.out.println("Value: " + getValue());
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        SubGenericClass<String> stringInstance = new SubGenericClass<>("Hello, Generics!");
        stringInstance.displayValue(); // 输出: Value: Hello, Generics!
        
        SubGenericClass<Integer> integerInstance = new SubGenericClass<>(123);
        integerInstance.displayValue(); // 输出: Value: 123
    }
}
示例 2:多个类型参数的泛型类
// 定义一个带有多个类型参数的泛型类
class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

// 从泛型类派生子类,子类也是泛型类
class ExtendedPair<K, V> extends Pair<K, V> {
    public ExtendedPair(K key, V value) {
        super(key, value);
    }

    public void display() {
        System.out.println("Key: " + getKey() + ", Value: " + getValue());
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        ExtendedPair<String, Integer> pair = new ExtendedPair<>("Age", 30);
        pair.display(); // 输出: Key: Age, Value: 30
        
        ExtendedPair<Double, String> anotherPair = new ExtendedPair<>(3.14, "Pi");
        anotherPair.display(); // 输出: Key: 3.14, Value: Pi
    }
}

子类不是泛型类

定义一个泛型类

子类不是泛型类时,父类的泛型类型必须指明

// 定义一个泛型类
class GenericClass<T> {
    private T value;

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

    public T getValue() {
        return value;
    }
}

从泛型类派生的子类
// 从泛型类派生子类
class StringGenericClass extends GenericClass<String> {
    public StringGenericClass(String value) {
        super(value);
    }

    public void printValue() {
        System.out.println("Value: " + getValue());
    }
}
调用子类方法
public class Main {
    public static void main(String[] args) {
        StringGenericClass stringInstance = new StringGenericClass("Hello, Generics!");
        stringInstance.printValue(); // 输出: Value: Hello, Generics!
    }
}
带多个类型参数的泛型类
// 定义一个带有多个类型参数的泛型类
class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

// 从泛型类派生子类
class StringIntegerPair extends Pair<String, Integer> {
    public StringIntegerPair(String key, Integer value) {
        super(key, value);
    }

    public void display() {
        System.out.println("Key: " + getKey() + ", Value: " + getValue());
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        StringIntegerPair pair = new StringIntegerPair("Age", 30);
        pair.display(); // 输出: Key: Age, Value: 30
    }
}

泛型接口

泛型接口语法

interface 接口名 <泛型标识1...>{

泛型标识1 方法名(泛型标识1 变量名)

}

泛型接口的使用情况(2种)

  • 实现类不是泛型

    • 接口类型必须明确,如果接口类型不明确,值实现类按Object处理
  • 实现类是泛型

实现类为泛型类

GenericInterface 是一个泛型接口,GenericClass 是一个泛型类,继承了这个接口。GenericClass 可以接受不同类型的参数。

// 定义一个泛型接口
interface GenericInterface<T> {
    void display(T value);
}

// 实现泛型接口的泛型类
class GenericClass<U> implements GenericInterface<U> {
    @Override
    public void display(U value) {
        System.out.println("Value: " + value);
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        GenericClass<String> stringInstance = new GenericClass<>();
        stringInstance.display("Hello, Generics!"); // 输出: Value: Hello, Generics!

        GenericClass<Integer> integerInstance = new GenericClass<>();
        integerInstance.display(123); // 输出: Value: 123
    }
}

实现类为普通类

ConcreteClass 是一个普通类,它实现了泛型接口 GenericInterface,并将类型参数指定为 String。

// 定义一个泛型接口
interface GenericInterface<T> {
    void display(T value);
}

// 实现泛型接口的普通类
class ConcreteClass implements GenericInterface<String> {
    @Override
    public void display(String value) {
        System.out.println("Value: " + value);
    }
}

// 使用示例
public class Main {
    public static void main(String[] args) {
        ConcreteClass concreteInstance = new ConcreteClass();
        concreteInstance.display("Hello, Generics!"); // 输出: Value: Hello, Generics!
    }
}

泛型方法

简单的泛型方法

public class GenericMethodExample {
    // 定义一个泛型方法
    public <T> void printArray(T[] array) {
        for (T element : array) {
            System.out.println(element);
        }
    }

    public static void main(String[] args) {
        GenericMethodExample example = new GenericMethodExample();

        // 使用泛型方法打印字符串数组
        String[] stringArray = {"Hello", "Generics", "!"};
        example.printArray(stringArray); // 输出: Hello, Generics, !

        // 使用泛型方法打印整数数组
        Integer[] intArray = {1, 2, 3, 4, 5};
        example.printArray(intArray); // 输出: 1, 2, 3, 4, 5
    }
}

带有多个类型参数的泛型方法

public class PairUtil {
    // 定义一个带有多个类型参数的泛型方法
    public static <K, V> void printPair(K key, V value) {
        System.out.println("Key: " + key + ", Value: " + value);
    }

    public static void main(String[] args) {
        // 使用泛型方法打印键值对
        printPair("Name", "Alice"); // 输出: Key: Name, Value: Alice
        printPair(1, 100);           // 输出: Key: 1, Value: 100
    }
}

泛型方法,是在调用方法的时候指明泛型的具体类型。重点看下泛型的方法(图参考自:https://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html)

  • 定义泛型方法语法格式

在这里插入图片描述

  • 调用泛型方法语法格式

在这里插入图片描述

说明一下,定义泛型方法时,必须在返回值前边加一个<T>,来声明这是一个泛型方法,持有一个泛型T,然后才可以用泛型T作为方法的返回值。

Class<T>的作用就是指明泛型的具体类型,而Class<T>类型的变量c,可以用来创建泛型类的对象。

Class 的基本概念

这里着重讲一下“Class” 的含义

  • Class 是一个表示某个类的对象,T 是这个类的类型参数。
  • 每个类在 Java 中都有一个对应的 Class 对象,这个对象包含了该类的结构信息,如字段、方法、构造函数等。

如何获取 Class 对象

  • 使用 ClassName.class
Class<String> stringClass = String.class;
  • 使用 getClass() 方法
String str = "Hello";
Class<?> stringClass = str.getClass();
  • 使用 Class.forName()
try {
    Class<?> stringClass = Class.forName("java.lang.String");
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

以下示例展示了如何使用 Class 来获取类的信息:

public class ClassExample {
    public static void main(String[] args) {
        // 获取 String 类的 Class 对象
        Class<String> stringClass = String.class;

        // 打印类的信息
        System.out.println("Class Name: " + stringClass.getName());
        System.out.println("Simple Name: " + stringClass.getSimpleName());
        System.out.println("Is Array: " + stringClass.isArray());
        System.out.println("Is Interface: " + stringClass.isInterface());
        System.out.println("Is Primitive: " + stringClass.isPrimitive());

        // 获取父类信息
        Class<?> superClass = stringClass.getSuperclass();
        System.out.println("Superclass: " + superClass.getName());

        // 获取实现的接口
        Class<?>[] interfaces = stringClass.getInterfaces();
        System.out.println("Interfaces:");
        for (Class<?> iface : interfaces) {
            System.out.println(" - " + iface.getName());
        }
    }
}

注意事项

Class 是 Java 反射机制中的一个重要组成部分,能够让程序在运行时与类进行交互,获取类的结构信息,极大地增强了 Java 的灵活性和动态性。通过理解 Class,开发者可以更有效地利用反射和泛型编程。

使用泛型来实现栈

泛型栈的定义

public class Stack<E> {
    private E[] arr = (E[]) new Object[10];
    //E[] arr = new E[10];是不允许的
    private int flag = 0;
    public void add(E x) {
        if(flag == arr.length ) {
            E[] arrnew = (E[]) new Object[arr.length * 2];
            for(int i = 0;i < arr.length;i++) {
                arrnew[i] = arr[i];
            }
            arr = arrnew;
        }
        arr[flag] = x;
        flag++;
    }
    public E get() {
        if(flag==0) {
            return null;
        }else {
            E x = arr[flag--];
            return x;
        }
    }
}

泛型栈的调用

public class Demo1 {
    public int x;
    public Demo1(int x) {
        this.x = x;
    }
    @Override
    public String toString() {
        return "Demo1 [x=" + x + "]";
    }
}
public class Test {
    public static void main(String[] args) {
        Stack<Demo1> xx = new Stack<>();
        for(int i = 0;i < 24;i++) {
            xx.add(new Demo1(i));
        }
        for(int i = 0;i < 24;i++) {
            System.out.println(xx.get());
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1952035.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

谷粒商城实战笔记-71-商品服务-API-属性分组-前端组件抽取父子组件交互

文章目录 一&#xff0c;一次性创建所有的菜单二&#xff0c;开发属性分组界面1&#xff0c;左侧三级分类树形组件2&#xff0c;右侧分组列表3&#xff0c;左右两部分通信3.1 子组件发送数据3.2&#xff0c;父组件接收数据 Vue的父子组件通信父组件向子组件传递数据子组件向父组…

SpringBoot添加密码安全配置以及Jwt配置

Maven仓库&#xff08;依赖查找&#xff09; 1、SpringBoot安全访问配置 首先添加依赖 spring-boot-starter-security 然后之后每次启动项目之后&#xff0c;访问任何的请求都会要求输入密码才能请求。&#xff08;如下&#xff09; 在没有配置的情况下&#xff0c;默认用户…

LLM agentic模式之工具使用: Gorilla

Gorilla Gorilla出自2023年5月的论文《Gorilla: Large Language Model Connected with Massive APIs》&#xff0c;针对LLM无法准确地生成API调用时的参数&#xff0c;构建API使用数据集后基于Llama微调了一个模型。 数据集构建 API数据集APIBench的构建过程如下&#xff1…

《Programming from the Ground Up》阅读笔记:p75-p87

《Programming from the Ground Up》学习第4天&#xff0c;p75-p87总结&#xff0c;总计13页。 一、技术总结 1.persistent data p75, Data which is stored in files is called persistent data, because it persists in files that remain on disk even when the program …

C语言程序设计15

程序设计15 问题15_1代码15_1结果15_1 问题15_2代码15_2结果15_2 问题15_3代码15_3结果15_3 问题15_1 在 m a i n main main 函数中将多次调用 f u n fun fun 函数&#xff0c;每调用一次&#xff0c;输出链表尾部结点中的数据&#xff0c;并释放该结点&#xff0c;使链表缩短…

【SQL 新手教程 3/20】关系模型 -- 外键

&#x1f497; 关系数据库建立在关系模型上⭐ 关系模型本质上就是若干个存储数据的二维表 记录 (Record)&#xff1a; 表的每一行称为记录&#xff08;Record&#xff09;&#xff0c;记录是一个逻辑意义上的数据 字段 (Column)&#xff1a;表的每一列称为字段&#xff08;Colu…

Buildroot 构建 Linux 系统

Buildroot 是一个工具&#xff0c;以简化和自动化为嵌入式系统构建完整 Linux 系统的过程。使用交叉编译技术&#xff0c;Buildroot 能够生成交叉编译工具链、根文件系统、Linux 内核映像和针对目标设备的引导加载程序。可以独立地使用这些选项的任何组合&#xff0c;例如&…

Vitis AI 使用 VAI_Q_PYTORCH 工具

目录 1. 简介 2. 资料汇总 3. 示例解释 3.1 快速上手示例 4. 总结 1. 简介 vai_q_pytorch 是 Vitis AI Quantizer for Pytorch 的缩写&#xff0c;主要作用是优化神经网络模型。它是 Vitis AI 平台的一部分&#xff0c;专注于神经网络的深度压缩。 vai_q_pytorch 的作用…

大数据管理中心设计规划方案(可编辑的43页PPT)

引言&#xff1a;随着企业业务的快速发展&#xff0c;数据量急剧增长&#xff0c;传统数据管理方式已无法满足高效处理和分析大数据的需求。建立一个集数据存储、处理、分析、可视化于一体的大数据管理中心&#xff0c;提升数据处理能力&#xff0c;加速业务决策过程&#xff0…

Spring Boot:图书管理系统(一)

1.编写用户登录接口 代码&#xff1a; package com.example.demo;import jakarta.servlet.http.HttpSession; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotatio…

HarmonyOS和OpenHarmony区别联系

前言 相信我们在刚开始接触鸿蒙开发的时候经常看到HarmonyOS和OpenHarmony频繁的出现在文章和文档之中&#xff0c;那么这两个名词分别是什么意思&#xff0c;他们之间又有什么联系呢&#xff1f;本文将通过现有的文章和网站内容并与Google的AOSP和Android做对比&#xff0c;带…

Vue.js 2 项目实战(五):水果购物车

前言 Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。它的设计目标是通过采用易于上手的结构和强大的功能&#xff0c;使前端开发变得更加简便和高效。以下是 Vue.js 的一些关键特性和优点&#xff1a; 核心特性 声明式渲染 Vue.js 使用声明式语法来描述用户界面&a…

AI周报(7.21-7.27)

AI应用-一款能提供情绪价值的智能鸟类喂食器&#xff08;Bird Buddy&#xff09; 图像识别技术&#xff1a;Bird Buddy装备了图像识别技术&#xff0c;能够识别超过1000种鸟类&#xff0c;涵盖了常见的鸟类品种。这种技术能够在鸟类经过时&#xff0c;通过内置麦克风捕捉的声音…

PID 控制实验 - 整定实验

Arduino PID Arduino-PID-LibraryArduino-PID-AutoTune-Library PID控制实验 – 制作测试台 PID Control Experiment – Making the Testing Rig PID (Proportional, Integral, Derivative) control is a classic control algorithm that I have used for a few projects,…

Debug-018-elementUI-el-tree中通过CSS隐藏任意一项的选择框checkbox

前情提要&#xff1a; 我们项目中使用的是elementUI&#xff0c;业务中经常需要使用到el-tree组件去实现一些有层级关系的功能。现在有一个需求描述一下&#xff1a;首先是这个el-tree是个有checkbox的树&#xff0c;每一子节点都可以被选择&#xff0c;用于去实现一些系统的权…

Shell编程之正则表达式与文本三剑客

目录 一、正则表达式 1.引言--什么是正则表达式 1.1正则表达式的功能 2.基础正则表达式&#xff08;BRE&#xff09; 2.1特殊字符 2.2定位符 2.3非打印字符 3.扩展正则表达式(ERE) 4.元字符操作的案列 二、命令小工具 1.cut&#xff1a;列截取工具 2.sort排序 …

vue3获取、设置元素高度

前言 在web端常见的需求场景中&#xff0c;会经常遇到table表格需要根据页面可视区域使高度自适应的情况。 傻喵(作者本人)昨天在尝试使用vue3实现这个需求时&#xff0c;看了几篇网上写的回答&#xff0c;都不太全面&#xff0c;所以干脆自己写个总结吧.(第一次写&#xff0c…

深入探讨 I/O 多路复用:提升系统 I/O 效率的关键技术

摘要 I/O&#xff08;输入/输出&#xff09;操作是计算机系统中不可或缺的一部分&#xff0c;而 I/O 多路复用技术则是提高系统 I/O 效率的重要手段。本文将浅谈 I/O 的基本概念&#xff0c;重点探讨 I/O 多路复用技术的原理、优势以及在现代系统中的应用。 引言 在现代计算…

php收银系统源码-收银员操作权限

收银系统是很多门店&#xff0c;尤其是连锁门店营业的必备工具&#xff0c;收银员每天需要通过收银系统记录商品的售卖数量&#xff0c;以及收款&#xff0c;会员开卡&#xff0c;核销订单等工作。但很多门店都不希望给收银员太高的权限&#xff0c;自然就离不开收银员的权限管…

flask开启调试模式,热部署

这里写自定义目录标题 功能快捷键 什么是热部署&#xff1f;简单说就是你程序改了&#xff0c;现在要重新启动服务器&#xff0c;嫌麻烦&#xff1f;不用重启&#xff0c;服务器会自己悄悄的把更新后的程序给重新加载一遍&#xff0c;这就是热部署&#xff08;抄的&#xff09;…