Java泛型(包括泛型+反射的应用)

news/2024/5/19 6:03:34 标签: java, 反射, 泛型

泛型简介

Java 泛型generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。

泛型的本质是参数化类型,也就是说,所操作的数据类型被指定为一个参数。在创建对象或调用方法的时候才明确具体的类型。

类型擦除

正确理解泛型概念的首要前提是理解类型擦除

更多关于类型擦除的问题,可以查看这篇文章:《Java泛型类型擦除以及类型擦除带来的问题》

什么是类型擦除

Java的泛型泛型,这是因为Java在编译期间,所有的泛型信息都会被擦除。Java的泛型基本上都是在编译器这个层次上实现的,在生成的字节码中是不包含泛型中的类型信息的。

  • 使用泛型的时候加上类型参数,在编译器编译的时候参数会被去掉,这个过程称为类型擦除

如在代码中定义List<String>等类型,在编译后都会变成List,由泛型附加的类型信息对JVM是看不到的JVM看到的只是List。Java编译器会在编译时尽可能的发现可能出错的地方,但是仍然无法在运行时刻出现的类型转换异常的情况,类型擦除也是Java的泛型与C++模板机制实现方式之间的重要区别。

通配符

更多关于Java 泛型中的通配符可以查看这篇文章:《聊一聊-JAVA 泛型中的通配符 T,E,K,V,?》

常用的通配符为: T、E、K、V、?

  • ? 表示不确定的 Java 类型
  • T 表示具体的一个Java类型(Type)
  • KV 分别代表 Java 键值中的 Key Value
  • E 代表Element

泛型的三种使用方式

泛型一般有三种使用方式:泛型泛型接口泛型方法

泛型

此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型

在实例化泛型类时,必须指定T的具体类型。

java">public class Generic<T>{ 

    private T key;

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

    public T getKey(){ 
        return key;
    }
}

实例化泛型类:

java">Generic<Integer> genericInteger = new Generic<Integer>(123456);

泛型接口

java">public interface Generator<T> {
    public T method();
}
  1. 实现泛型接口,不指定类型:
java">class GeneratorImpl<T> implements Generator<T>{
    @Override
    public T method() {
        return null;
    }
}
  1. 实现泛型接口,指定类型:
java">class GeneratorImpl<T> implements Generator<String>{
    @Override
    public String method() {
        return "hello";
    }
}

泛型方法

java">public static <E> void printArray(E[] inputArray){         
    for(E element : inputArray){        
      	System.out.printf( "%s ", element );
    }
    System.out.println();
}

使用:

java">// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3 };
String[] stringArray = { "Hello", "World" };
printArray(intArray); 
printArray(stringArray); 

泛型的优点

使用泛型的好处就是:

  1. 代码更加简洁,不再需要强制类型转换

  2. 程序更加健壮,在编译期间没有警告,在运行期就不会出现ClassCastException异常

  3. 提高代码的复用性,比如:在动态代理中,通过泛型可以代理任何需要被代理的类

java">public class ServiceProxy<T> implements InvocationHandler {
  	private T target;
  	public ServiceProxy(T target) {
        this.target = target;
    }
}

泛型的应用

  1. 操作集合时

容器中之所以可以存储各种类型的对象,就是因为引入了泛型

java">List lists = new ArrayList<String>();
  1. 用于基础组件时

因为组件的要求是需要做到一定的通用性,需要支持不同的类型,而泛型的特点是:在创建对象或调用方法的时候才明确具体的类型。可以参考SpringData JPAJpaRepository的写法:

java">public interface JpaRepository<T, ID> extends 
  PagingAndSortingRepository<T, ID>, QueryExampleExecutor<T> {
  	List<T> findAll();
  	List<T> findAll(Sort sort);
  	List<T> findAllById(Iterable<ID> ids);
  	<S extends T> List<S> saveAll(Iterable<S> entities);
  	void flush();
   	<S extends T> S saveAndFlush(S entity);
   	void deleteInBatch(Iterable<T> entities);
   	void deleteAllInBatch();
   	T getOne(ID id);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example);
  
   	@Override
   	<S extends T> List<S> findAll(Example<S> example, Sort sort);
}

此外,在组件中,还离不开Java的反射机制,一般是反射+泛型

再举个实际的例子

比如有个需求是将某些数据库表的某些字段进行聚合,SQL语句就是

select sum(column1),sum(column2) from table group by field1,field2

需要sumgroup by 的列是由业务方自己传入,而SQL表其实就是我们的POJO,传入的字段肯定也是POJO的属性,单个业务实际可以在参数上将POJO写死,但是如果参数设置为泛型,便可以提高代码的复用性。

拿到参数后,通过反射获取其字段具体的值,就可以做累加了。具体代码示例如下:

java">// 传入需要 group by 和 sum 的字段名
public cacheMap(List<String> groupByKeys, List<String> sumValues) {
  	this.groupByKeys = groupByKeys;
  	this.sumValues = sumValues;
}

private void excute(T e) {
  
    // 从pojo 取出需要group by 的字段 list
    List<Object> key = buildPrimaryKey(e);

    // primaryMap 是存储结果的Map
    T value = primaryMap.get(key);

    // 如果从存储结果找到有相应记录
    if (value != null) {
        for (String elem : sumValues) {
          // 反射获取对应的字段,做累加处理
            Field field = getDeclaredField(elem, e);
            if (field.get(e) instanceof Integer) {
                field.set(value, (Integer) field.get(e) + (Integer) field.get(value));
            } else if (field.get(e) instanceof Long) {
                field.set(value, (Long) field.get(e) + (Long) field.get(value));
            } else {
                throw new RuntimeException("类型异常,请处理异常");
            }
      	}
        // 处理时间记录
        Field field = getDeclaredField("updated", value);
        if (null != field) {
            field.set(value, DateTimeUtils.getCurrentTime());
        }
    } else {
      	// group by 字段 第一次进来
        try {
            primaryMap.put(key, Tclone(e));
            createdMap.put(key, DateTimeUtils.getCurrentTime());
        }catch (Exception ex) {
         	 	log.info("first put value error {}" , e);
        }
    }
}

参考:https://mp.weixin.qq.com/s/fL6cMjSNAX_fZ7wFBJO80Q


http://www.niftyadmin.cn/n/1690223.html

相关文章

到底什么是 JavaBean

JavaBean其实就是特殊的Java类&#xff0c;是一种规范&#xff0c;也可称为一种技术。 与其它 Java 类相比JavaBean需满足以下条件&#xff1a; 必须提供无参构造方法类中属性都必须私有化&#xff08;private&#xff09;需要被序列化且实现 Serializable 接口可能有一系列 …

坚持看完保证你能深刻理解Java的反射机制

静态语言 & 动态语言 在讲反射之前先来了解一下什么是静态语言和动态语言 动态语言 是一类在运行时可以改变其结构的语言&#xff0c;例如新的函数、对象、甚至代码都可以被引进&#xff0c;已有的函数可以被删除&#xff0c;或是其他结构上的变化&#xff0c; 通俗地讲就…

MacOS 终端导入/导出 sql 脚本

终端导入sql脚本 比如导入一个mysql脚本文件casaba.sql&#xff0c;路径为/Users/superfarr/Desktop/casaba.sql 如果脚本中有创建数据库的语句&#xff0c;我们就不需要再次创建&#xff0c;如果没有&#xff0c;我们需要自己创建 下面的流程为针对脚本中没有创建数据库语句…

SpringBoot + Mybatis + Thymeleaf 搭建个人博客——Iceberg-Blog

Iceberg-BlogWhy Iceberg Blog 为什么叫 Iceberg Blog &#xff1f; 学无止境&#xff0c;无论何时&#xff0c;我们都会感到知识就像一座冰山&#xff0c;我们学到的只是冰山一角。 博客简介 项⽬描述&#xff1a;采⽤前后端分离架构实现的博客系统&#xff0c;主体架构采用…

MyBatis 中 #{} 和 ${} 区别

二者区别 #{} 是预编译处理&#xff0c;传进来的数据会加个" " #将传入的数据都当成一个字符串&#xff0c;会对自动传入的数据加一个双引号 ${}就是字符串替换&#xff0c;直接替换掉占位符 $方式一般用于传入数据库对象&#xff0c;例如传入表名 SQL 注入 使…

JavaOJ训练——输入一串以逗号隔开数字然后存入数组中并输出

代码&#xff1a; public class OJ {public static void main(String[] args){Scanner sc new Scanner(System.in);System.out.println("请输入一串用英文逗号隔开的整数&#xff1a;");String input sc.next();String[] strArr input.split(",");int[…

LeetCode系列之「反转链表」

剑指 Offer 24. 反转链表 ListNode // Definition for singly-linked list. public class ListNode {int val;ListNode next;ListNode(int x) { val x; } }一、迭代解法&#xff1a; class Solution {public ListNode reverseList(ListNode head) {if(head null) return n…

Jupyter Notebook在清华镜像下安装Nbextensions 插件

清华镜像下安装Nbextensions Nbextensions 有很多好用的扩展功能,比如显示目录,显示程序执行时间(Execute Time),美化格式(Code prettify),代码折叠(Codefolding)等。 但是直接安装比较慢,我们利用镜像安装。 在终端执行: pip install jupyter_contrib_nbexten…