概述
泛型是JDK1.5以后才有的, 可以在编译时期进行类型检查,且可以避免频繁类型转化!
// 集合的声明 不使用泛型
List list = new ArrayList();
list.add("evan_qb");
list.add(1);
// 集合的使用 当不使用泛型时,会抛出运行时异常
String str = (String) list.get(1);
// 使用泛型
// 声明泛型集合的时候指定元素的类型 使用泛型后,当存储不同类型时,编译时期就会报错
List<String> list = new ArrayList<String>();
list.add("China");
//list.add(1);// 编译时期报错
String str = list.get(1);
在运行时抛异常,和在编译时报错,显然,在编译时报错更容易定位错误
而且使用泛型也易于维护,不再鱼龙混杂,而是存储相同类型,易于维护,可以让你消除代码中的强制类型转换,同时获得一个附加的类型检查层,该检查层可以防止有人将错误类型的键或值保存在集合中。
泛型擦除
泛型只在编译时期有效,编译后的字节码文件中不存在有泛型信息!
泛型擦除实例
public void save(List<Person> p){
}
public void save(List<Dept> d){ // 报错: 与上面方法编译后一样
}
泛型写法
正确写法:
List<Object> list = new ArrayList<Object>();
List<String> list1 = new ArrayList<String>();
List list2 = new ArrayList<String>();
错误写法:
//泛型类型要一致
List<Object> list4 = new ArrayList<String>();
// 泛型类型必须是引用类型,不能为基本类型
List<int> list5 = new ArrayList<int>();
泛型方法/泛型类/泛型接口
泛型方法:
// 定义泛型方法
public <K,T> T save(T t,K k) {
return null;
}
// 使用泛型方法: 在使用泛型方法的时候,确定泛型类型
save(1.0f, 1);
泛型类:
public class GenericDemo<T> {
// 定义泛型方法
public <K> T save(T t,K k) {
return null;
}
}
// 泛型类: 在创建爱泛型类对象的时候,确定类型
GenericDemo<String> demo = new GenericDemo<String>();
泛型接口:
//定义泛型接口
public interface IBaseDao<T> {
void save(T t );
void update(T t );
}
泛型接口类型确定:
实现泛型接口的类也是抽象,那么类型在具体的实现中确定或创建泛型类的时候确定
public class BaseDao<T> implements IBaseDao<T> {}
在业务实现类中直接确定接口的类型
public class PersonDao implements IBaseDao<Person>{}
泛型关键字:
在泛型中:
? 指定只是接收值
extends 元素的类型必须继承自指定的类
super 元素的类型必须是指定的类的父类
关键字 ?
//只带泛型特征的方法
public void save(List<?> list) {
// 只能获取、迭代list; 不能编辑list
}
// ? 可以接收任何泛型集合, 但是不能编辑集合值; 所以一般在方法参数中用
List<?> list = new ArrayList<String>();
//list.add("");// 报错
关键字: extends【上限】
list集合只能处理 Double/Float/Integer等类型
限定元素范围:元素的类型要继承自Number类 (上限)
public void save(List<? extends Number> list) {
}
List<Double> list_1 = new ArrayList<Double>();
List<Float> list_2 = new ArrayList<Float>();
List<Integer> list_3 = new ArrayList<Integer>();
List<String> list_4 = new ArrayList<String>();
// 调用
save(list_1);
save(list_2);
save(list_3);
save(list_4); //报错
关键字 : super【下限】
super限定元素范围:必须是父类(Object)或者本身(String)【下限】
public void save(List<? super String> list) {
}
// 调用上面方法,必须传入String的父类
List<Object> list1 = new ArrayList<Object>();
List<String> list2 = new ArrayList<String>();
List<Integer> list3 = new ArrayList<Integer>();
save(list3); //报错
泛型的反射
Type 接口,任何类型默认的接口!
包括: 引用类型、原始类型、参数化类型
List<String> list = new ArrayList<String>();
泛型集合: list
集合元素定义:new ArrayList<String>(); 中的String
参数化类型: ParameterizedType
即:“ArrayList<String>” 为参数化类型
泛型反射的案例:通用的jdbc方法
我们使用一个通用的BaseDao方法去存储基本的jdbc方法
由于本案例使用的数据库时Oracle数据库,还有DBUtil组件,所以需要导入相应的包
下面先写BaseDao类
public class BaseDao<T> {
private Class clazz;
private String tableName;
/**
* 构造函数:
* 1. 获取当前运行类的参数化类型;
* 2. 获取参数化类型中实际类型的定义(class)
*/
public BaseDao(){
// this 表示当前运行类 (AccountDao/StudentDao)
// this.getClass() 当前运行类的字节码(AccountDao.class/StudentDao.class)
// this.getClass().getGenericSuperclass(); 当前运行类的父类,即为BaseDao<Account>
// ParameterizedType,其实就是“参数化类型”
//获取当前运行类的父类,Type类型表示任意类型
Type type = this.getClass().getGenericSuperclass();
//强制转换为ParameterizedType(参数化)类型
ParameterizedType pt = (ParameterizedType) type;
// 获取参数化类型中类似【new Type[]{Account.class}】,获取实际的参数类型
Type[] types = pt.getActualTypeArguments();
//获取数据的第一个元素:Account.class
clazz = (Class) types[0];
//获取表名
tableName = clazz.getSimpleName();
}
public T findById(int id){
Connection conn = null;
String sql = "select * from " + tableName + " where id = ?";
try {
conn = ConnectionFactory.getConnection();
System.out.println(tableName);
QueryRunner qr = new QueryRunner();
return qr.query(conn,sql, new BeanHandler<T>(clazz),id);
} catch (SQLException e) {
throw new RuntimeException(e);
}finally{
ConnectionFactory.close(conn, null, null, null);
}
}
/**
* 获取所有
* @return
*/
public List<T> getAll(){
Connection conn = null;
String sql = "select * from " + tableName;
try {
conn = ConnectionFactory.getConnection();
return new QueryRunner().query(conn, sql,new BeanListHandler<T>(clazz));
} catch (SQLException e) {
throw new RuntimeException(e);
}finally{
ConnectionFactory.close(conn, null, null, null);
}
}
}
当谁需要用到这些方法时,我们就将其继承BaseDao,然后我们可以通过反射泛型的技术,或者正在运行的类的类型,然后进行相应的CRUD操作,而每个子类只需要定义特有的jdbc的方法就行了
分别编写Account类和Student类继承BaseDao类
public class AccountDao extends BaseDao<Account>{
}
public class StudentDao extends BaseDao<Student>{
}
Student实体类
public class Student {
private int id;
private String stuName;
private double money;
//省略getter和setter方法构造方法toString()方法
}
Account实体类
public class Account {
private int id;
private String accountName;
private double money;
//省略getter和setter方法构造方法toString()方法
}
对应的创建表的语句:
create sequence account_seq increment by 1 start with 1;
create sequence stu_seq increment by 1 start with 1;
create table account(
id number(10),
accountName varchar2(20),
money number(10,2)
);
create table Student(
id number(10),
stuName varchar2(20),
money number(10,2)
);
drop table student;
-- 插入数据到account表中
insert into account values(account_seq.nextval,'张三',1000.00);
insert into account values(account_seq.nextval,'李四',2000.00);
--插入数据到Student表中
insert into student values(stu_seq.nextval,'evan_qb',500.00);
insert into student values(stu_seq.nextval,'李四',1000.00);
commit
然后我们就来测试一波
StudentDao stuDao = new StudentDao();
List<Student> stus = stuDao.getAll();
for (Student stu : stus) {
System.out.println(stu);
运行结果如下:
AccountDao account = new AccountDao();
Account account1 = account.findById(1);
//List<Account> accounts = account.getAll();
System.out.println(account1);
运行结果如下:
这样,我们就用反射泛型实现了对jdbc的优化