使用java泛型设计通用方法
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张student表.
user:

student:

如果要根据id查询数据, 你会怎么做呢?写2个方法分别查询user和student?其实这时候我们就可以使用泛型和反射写一个通用的方法.
user的实体类:
private Integer id;
private String username;
private String password;
private String hobby;
//getXxx方法和setXxx方法
student实体类:
private Integer id;
private String name;
private Integer age;
//getXxx方法和setXxx方法
BaseDao接口:
public interface BaseDao<T> {
//根据id查询的方法
T findById(Integer id);
}
BaseDaoImpl类, 实现了BaseDao接口, 通用方法就在这里面完成:
//设置为抽象的, 不让他实例化, 让其子类实例化就行了
//通过泛型设计通用方法的关键就是利用反射拿到泛型的具体类型
public abstract class BaseDaoImpl<T> implements BaseDao<T> {
private String tableName; //表名
private Class<T> actualType;//真实类型 /**
* findById(Integer id)这个方法被子类继承了, 假设我们在UserDaoImpl中操作, 此时参数化类型T为User
* 下面的讲解都假设是在UserDaoImpl中进行的
*/
//把公共部分可以放到构造方法中
@SuppressWarnings("unchecked")
public BaseDaoImpl() {
//返回类型是Type 是 Java 编程语言中所有类型的公共高级接口. 它们包括原始类型、参数化类型、数组类型、类型变量和基本类型.
//Type的已知子接口: ParameterizedType 表示参数化类型, 如 Collection<String>.
//getClass()得到UserDaoImpl的Class, 得到Class一般有3中方式: getClass(), 类名.class, Class.forName()
Type type = getClass().getGenericSuperclass();//获取UserDaoImpl<User>的参数化类型的父类BaseDaoImpl<User>
//由于我们得到的是一个参数化类型, 所以转成ParameterizedType, 因为需要使用里面的方法
ParameterizedType pt = (ParameterizedType) type;//强转
Type[] actualTypeArr = pt.getActualTypeArguments();//获取真实参数类型数组[User.class], 可以调试看到这里的值
actualType = (Class<T>) actualTypeArr[0];//数组只有一个元素
tableName = actualType.getSimpleName();
} @Override
public T findById(Integer id) {
String sql = "select * from " + tableName + " where id = ?";
try {
return QRUtils.getQueryRunner().query(sql, new BeanHandler<T>(actualType), id);
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
}
连接数据库操作是用的c3p0和dbutils, 有关这方面的内容可以参看我以前的随笔.
UserDao接口, 继承BaseDao接口:
public interface UserDao<T> extends BaseDao<T> {
}
UserDaoImpl类, 实现UserDao接口, 继承BaseDaoImpl类, 可以看到里面什么方法也没有:
public class UserDaoImpl extends BaseDaoImpl<User> implements UserDao<User> {
}
StudentDao接口, 继承BaseDao接口:
public interface StudentDao<T> extends BaseDao<T> {
}
StudentDaoImpl类, 实现StudentDao接口, 继承BaseDaoImpl类, 也可以看到里面什么方法也没有:
public class StudentDaoImpl extends BaseDaoImpl<Student> implements StudentDao<Student> {
}
从以上dao可以看到, 我是在继承BaseDaoImpl类时把泛型具体化了.
测试:
@Test
public void testGeneric() throws Exception {
UserDao<User> userDao = new UserDaoImpl();
System.out.println(userDao.findById(1)); System.out.println("-------------------");
StudentDao<Student> studentDao = new StudentDaoImpl();
System.out.println(studentDao.findById(1));
}
看一下测试的结果: 同一个方法把2张表中的数据都查出来了

有关泛型的基本使用, 请参考java泛型基础
使用java泛型设计通用方法的更多相关文章
- 初识Java泛型以及桥接方法
泛型的由来 在编写程序时,可能会有这样的需求:容器类,比如java中常见的list等.为了使容器可以保存多种类型的数据,需要编写多种容器类,每一个容器类中规定好了可以操作的数据类型.此时可能会有Int ...
- java导出excel通用方法
首先需要引入的jar包: 正式代码了. import java.io.FileOutputStream; import java.io.OutputStream; import java.net.UR ...
- Effective Java - [3. 对象通用方法]
Item 10. 若覆盖equals方法,需要遵守规则
- java泛型基础
泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法. Ja ...
- java 泛型的类型擦除与桥方法
泛型类 --代码参考:java核心技术 卷1 第十版 public class Pair<T> { private T first; private T second; //构造器 pub ...
- Java泛型-内部原理: 类型擦除以及类型擦除带来的问题
一:Java泛型的实现方法:类型擦除 大家都知道,Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,正确理解泛型概念的首要前提是理解类型擦除.Java的泛型基本上都是在编译 ...
- java泛型 8 泛型的内部原理:类型擦除以及类型擦除带来的问题
参考:java核心技术 一.Java泛型的实现方法:类型擦除 前面已经说了,Java的泛型是伪泛型.为什么说Java的泛型是伪泛型呢?因为,在编译期间,所有的泛型信息都会被擦除掉.正确理解泛型概念的首 ...
- Java泛型学习笔记--Java泛型和C#泛型比较学习(一)
总结Java的泛型前,先简单的介绍下C#的泛型,通过对比,比较学习Java泛型的目的和设计意图.C#泛型是C#语言2.0和通用语言运行时(CLR)同时支持的一个特性(这一点是导致C#泛型和Java泛型 ...
- java泛型(二)、泛型的内部原理:类型擦除以及类型擦除带来的问题
微信公众号[程序员江湖] 作者黄小斜,斜杠青年,某985硕士,阿里 Java 研发工程师,于 2018 年秋招拿到 BAT 头条.网易.滴滴等 8 个大厂 offer,目前致力于分享这几年的学习经验. ...
随机推荐
- [Unity3D]利用Raycast实现物体的选择与操作
本文系作者原创 转载请注明出处 如果是一个2D的平面项目或者说需要在三维空间选择一个物体时(经常表现为抓取物件),我们需要用到Raycast事件 那么首先先说说什么是Raycast 按照字面上来理解的 ...
- 第14章 Linux启动管理(3)_系统修复模式
3. 系统修复模式 3.1 单用户模式 (1)在grub界面中选择第2项,并按"e键"进入编辑.并在"-quiet"后面加入" 1",即&q ...
- 解决Chrome 下载带半角分号出现net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION的问题
方式一:添加双引号Response.AddHeader("content-disposition", "attachment; filename=\"" ...
- 【repost】JS错误类型的学习
SyntaxError是解析代码时发生的语法错误 // 变量名错误 var 1a; // 缺少括号 console.log 'hello'); (2)ReferenceError Referen ...
- 【C#】获取网页内容及HTML解析器HtmlAgilityPack的使用
最近经常需要下载一些东西,而这个下载地址又会经过层层跳转,每个页面上都有很多广告,烦不胜烦,所以做了一个一键获得最终下载地址的小工具.使用C#,来获取网页内容,然后通过HtmlAgilityPack获 ...
- 前端如何正确选择offer,到底选哪个?
文章背景:来自于一次线上交流,当时回答感觉比较粗糙,做个阶段性的总结,也分享给其它朋友. 当时的题目是,共2个offer,如何选择: 1. 美团外卖前端 2. 京东深圳前端研发(只有通过邮件,还有收到 ...
- SVG:textPath深入理解
SVG的文本可以沿着一条自定义的Path来排布,比如曲线.圆形等等,使用方式如下所示(来源MDN): <svg viewBox="0 0 1000 300" xmlns=&q ...
- 2000条你应知的WPF小姿势 基础篇<74-77 WPF 多窗口Tips>
在正文开始之前需要介绍一个人:Sean Sexton. 来自明尼苏达双城的软件工程师.最为出色的是他维护了两个博客:2,000ThingsYou Should Know About C# 和 2,00 ...
- MapReduce剖析笔记之四:TaskTracker通过心跳机制获取任务的流程
上一节分析到了JobTracker把作业从队列里取出来并进行了初始化,所谓的初始化,主要是获取了Map.Reduce任务的数量,并统计了哪些DataNode所在的服务器可以处理哪些Split等等,将这 ...
- [转]Java实现定时任务的三种方法
在应用里经常都有用到在后台跑定时任务的需求.举个例子,比如需要在服务后台跑一个定时任务来进行非实时计算,清除临时数据.文件等.在本文里,我会给大家介绍3种不同的实现方法: 普通thread实现 Tim ...