一 概述
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。 Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。如下图描述:
二 使用泛型
1.声明
interface List<T> 和 class TestGen<K,V> ,其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。
2.泛型的实例化
一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
T只能是类,不能用基本数据类型填充。
注:
1.对象实例化时不指定泛型,默认为:Object。
2.泛型不同的引用不能相互赋值。
3.加入集合中的对象类型必须与指定的泛型类型一致。
4.静态方法中不能使用类的泛型。
5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
6.不能在catch中使用泛型
7.从泛型类派生子类,泛型类型需具体化
三 自定义泛型
下面举一个例子:
Dao.java
package com.lxj.generics;
//父接口
public interface Dao<T> {
//泛型方法
public <T> T getT(T t);
}
JdbcDaoImpl.java
package com.lxj.generics;
public class JdbcDaoImpl<T> implements Dao<T>{
@Override
public <T> T getT(T t) {
return t;
}
}
CustomerDao.java
package com.lxj.generics;
public class CustomerDao extends JdbcDaoImpl<Customer>{
}
PersonDao.java
package com.lxj.generics;
public class PersonDao extends JdbcDaoImpl<Person>{
}
Customer.java
package com.lxj.generics;
public class Customer {
private int id;
private String name;
private Integer age;
public Customer() {
}
public Customer(int id,String name,Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Customer [id=" + id + ", name=" + name + ", age=" + age + "]";
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Person.java
package com.lxj.generics;
public class Person {
private int id;
private String name;
private Integer age;
public Person() {
}
public Person(int id,String name,Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
TestGenerics.java
package com.lxj.generics;
public class TestGenerics {
public static void main(String[] args) {
CustomerDao customerDao = new CustomerDao();
Customer customer = customerDao.getT(new Customer(1,"zhangsnag",4));
System.out.println(customer);
PersonDao personDao = new PersonDao();
Person person = personDao.getT(new Person(1,"zhangsnag",4));
System.out.println(person);
}
}
运行结果:
Customer [id=1, name=zhangsnag, age=4]
Person [id=1, name=zhangsnag, age=4]
从这个例子我们会发现JdbcDaoImpl<T>具有通用性,假如是CustomerDao,直接继承JdbcDaoImpl就行了,如果想扩展,加一个PersonDao,直接继承JdbcDaoImpl就可以,写明泛型类型就好了,通过泛型类型,JVM能够识别不同的类型。
四 通配符
1.使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
Ø唯一的例外是null,它是所有类型的成员。
将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
唯一的例外的是null,它是所有类型的成员。另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object。
<?> 允许所有泛型的引用调用
<? extends Number> (无穷小 , Number]
只允许泛型为Number及Number子类的引用调用
<? super Number> [Number , 无穷大)
只允许泛型为Number及Number父类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用
同样是之前的例子:
新增加一个 Animal.java 类
package com.lxj.generics;
public class Animal {
}
Customer 类 Person类都继承Animal 类,JdbcDaoImpl 类的泛型如下:
package com.lxj.generics;
public class JdbcDaoImpl<T extends Animal> implements Dao<T>{
@Override
public <T> T getT(T t) {
return t;
}
}