Java泛型(Generics)是JDK1.5 引入的一个新特性,泛型提供了编译时类型安全监测机制,该机制允许我们在编译时检测到非法的类型数据。
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数。
泛型的优点 :
- 将类型的检查从运行期提前到了编译期,保证数据类型的安全
- 消除了强制类型转换的风险
泛型可以使用在类、接口、方法 、属性
1、泛型类
语法
泛型类常用的泛型标识 : T 、E、 K、 V (其他大字母也可以的)class 类名 <泛型标识, 泛型标识 , ...> {
private 泛型标识 变量名;
.....
}
泛型类使用语法
JDK1.7之后 :类名<具体数据类型> 对象名 = new 类名<具体的数据类型>();
类名<具体数据类型> 对象名 = new 类名<>();
泛型类使用注意事项
- 泛型类如果没有指定具体的数据类型,则默认操作的是Object类型
- 泛型的参数只能是引用类型,不能是基本数据类型
- 泛型在逻辑上可以看成是多个不同的类型,但实际上都是相同的类型
/**
*
* @author Administrator
* T 和 E 的名称 可以随便起名 大小写都可以 ,但是泛型一般定义成大写
* @param <T>
* @param <E>
*/
public class Point<T,E> {
private T x;
private E y;
public T getX() {
return x;
}
public void setX(T x) {
this.x = x;
}
public E getY() {
return y;
}
public void setY(E y) {
this.y = y;
}
@Override
public String toString() {
return "Point [x=" + x + ", y=" + y + "]";
}
public Point(T x, E y) {
super();
this.x = x;
this.y = y;
}
public Point() {
super();
// TODO Auto-generated constructor stub
}
}
测试类:
public class TestPoint {
public static void main(String[] args) {
//创建对象的时候 指定类型 new 对象时 可以不指定 (JDK1.7以上版本你可以不)
Point<Integer,Integer> p1 =new Point<>(10,8);
System.out.println(p1);
Point<String,String> p2 = new Point<>("东经100","北纬20");
System.out.println(p2);
String x = p2.getX();
Point<String, Integer> p3 = new Point<>("东经200",100);
System.out.println(p3);
}
}
/**
* 定义统一返回值
* @author Administrator
*
* @param <T>
*/
public class ResponseResult<T> {
private int code ;
private String msg;
private T data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public ResponseResult(int code, String msg, T data) {
super();
this.code = code;
this.msg = msg;
this.data = data;
}
public ResponseResult() {
super();
// TODO Auto-generated constructor stub
}
}
public class TestResult {
public static void main(String[] args) {
//返回用户数据
ResponseResult<User> res1 = new ResponseResult<>(200, "查询成功", new User());
//返回订单对象
ResponseResult<Order> res2 = new ResponseResult<>(200,"查询订单成功",new Order());
}
}
类上的泛型一般不超过两个
2、泛型接口
语法
interface 接口名 <泛型标识, 泛型标识 , ...> { 泛型标识 方法名(泛型标识 变量); ..... }
泛型接口的使用
- 实现类不是泛型类, 接口要明确数据类型
- 实现类也是泛型类,实现类和接口的泛型要一致
案例
指定类型的实现类 :
不指定类型的实现类 (泛型类):/** * 操作数据库 增删改查 * @author Administrator * */ public interface IBaseDao<T> { void insert(T t); void update(T t); void deleteByKey(int id); T selectByKey(int id); T[] selectAll(); }
```java /**
- 实现类没有使用泛型 必须在接口上指定类型
- @author Administrator
/
public class UserDaoImpl implements IBaseDao
{
@Override public void insert(User t) {
// TODO Auto-generated method stub
}
@Override public void update(User t) {
// TODO Auto-generated method stub
}
@Override public void deleteByKey(int id) {
// TODO Auto-generated method stub
}
@Override public User selectByKey(int id) {
// TODO Auto-generated method stub return null;
}
@Override public User[] selectAll() {
// TODO Auto-generated method stub return null;
}
}
```java
/**
* 实现类的泛型 和 接口的泛型 必须一致
* @author Administrator
*
* @param <T>
*/
public class BaseImpl<T> implements IBaseDao<T> {
@Override
public void insert(T t) {
// TODO Auto-generated method stub
System.out.println("插入数据成功..."+t);
}
@Override
public void update(T t) {
// TODO Auto-generated method stub
}
@Override
public void deleteByKey(int id) {
// TODO Auto-generated method stub
}
@Override
public T selectByKey(int id) {
// TODO Auto-generated method stub
return null;
}
@Override
public T[] selectAll() {
// TODO Auto-generated method stub
return null;
}
}
3、泛型方法
首先我们做一个方法的区分,以前在泛型类中使用的方法并不是泛型方法,只是泛型类中的成员方法
泛型类 :在实例化类的时候指明泛型的具体类型
泛型方法 :在调用方法的时候指明泛型的具体类型
语法定义
修饰符 <T, E , ...> 返回值类型 方法名(形参列表){ 方法体... }
泛型方法使用注意事项
- 修饰符与返回值类型中间定义的
<T,E...>
就是在声明此方法为泛型方法 - 只有声明了
的方法才是泛型方法,泛型类中使用了泛型的方法不是泛型方法,而是类的成员方法 <T>
表示该方法在定义时可以使用泛型T, 这时就可以在方法中使用泛型类型T了- 泛型方法能使方法独立于类的泛型单独使用
- 如果static修饰的方法使用泛型,则该方法必须声明为泛型方法 在泛型类中声明了静态方法 ,必须声明成 泛型方法
- 修饰符与返回值类型中间定义的
public class Container<T> {
// 成员方法: 使用了泛型做形参的类型, 泛型做方法的返回类型
public T getValue(T data) {
return data;
}
// 泛型方法 : 在修饰符与方法的返回类型中间声明一个<泛型标识>
// 泛型方法的泛型类型是在真正调用方法的时候确定的,与类的泛型无关
public <E> E getKey(E key) {
return key;
}
// 使用了泛型类若想声明为静态方法, 则该方法必须是泛型方法
public static <E> E getStaticKey(E key) {
return key;
}
}
4、通配符
泛型通配符一般使用 ? 代替具体的类型参数。所以,类型通配符是类型实参,而不是类型形参
通配符使用的注意事项
编译错误:不能用在泛型方法声明上,返回值类型前面<>不能使用?
public static <?> void test(ArrayList<?> list){}
编译错误:不能用在泛型类的声明上
public class CommonTypeClass<?>{ }
编译错误:不能用在创建对象上,右边属于创建集合对象
ArrayList<?> list2 = new ArrayList<?>();
只能用在声明类型(属性)、方法参数上,不能用在定义泛型类上
4.1 无边界通配符
无边界通配符 就是 ?
:
public class MyResult {
private String name ;
private int code ;
private List<?> list; // 使用在属性上
//省略 set 、get方法
//使用在方法的参数上
public void parseData(List<?> lists){
for(Object o :lists){
System.out.println(o);
}
}
}
4.2 上限通配符
语法 :
类/接口<? extends 实参类型>
限制数据的赋值 必须是 限制类及其子类 :
public class ResultData {
private List<? extends Dog> lists ; //上限统配符
public void setLists(List<? extends Dog> lists) {
this.lists = lists;
}
public List<? extends Dog> getLists() {
return lists;
}
}
public class TestResultData {
public static void main(String[] args) {
/*
* private List<? extends Dog> lists ; //上限统配符
*/
ResultData data = new ResultData();
//创建的一个 Taidi的集合 他是Dog子类
List<Taidi> td = new ArrayList<>();
td.add(new Taidi());
td.add(new Taidi());
data.setLists(td);
//创建一个 Animal的集合
List<Animal> as = new ArrayList<>();
as.add(new Animal());
//data.setLists(as); 因为 ResultData中的lists 集合 限制了 集合中的数据 那必须是Dog及其子类
}
}
4.3 、下限通配符
语法 :
类/接口<? super 实参类型>
限制数据赋值时 必须是 类型本身及其父类
public class ResultData {
private List<? super Dog> lists ; //下限统配符
public List<? super Dog> getLists() {
return lists;
}
public void setLists(List<? super Dog> lists) {
this.lists = lists;
}
}
public class TestResultData {
public static void main(String[] args) {
/*
* private List<? super Dog> lists ; //下限统配符
*/
ResultData data = new ResultData();
//创建的一个 Taidi的集合 他是Dog子类
List<Taidi> td = new ArrayList<>();
td.add(new Taidi());
td.add(new Taidi());
//data.setLists(td); lists属性 限制的是 必须是 Dog 及其父类
//创建一个 Animal的集合
List<Animal> as = new ArrayList<>();
as.add(new Animal());
data.setLists(as);
}
}
5、泛型的擦除
泛型是在 Java编译的时候 ,根据泛型进行类型检查 ,把一些类型不匹配的错误 ,提前到编译期就呈现出来 ,但是一旦编译成了class文件,在class文件中不会有泛型 。
也就是说 泛型虽然多了检查类型这个功能,但是对程序运行的效率没有任何影响