一:概述
/**
* 在jdk1.5之前,我们在往一个集合中存放一个对象或者基本数据类型的时候,取出来的时候,如果数据类型
* 不匹配,那么就不可避免的需要做一些强制类型转换的工作,那么这就不可避免的出现类型转换异常。
*/
二:Demo1
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
import java.util.ArrayList;
import java.util.List;
/**
* 为何我们需要使用泛型?
* User: OF895
* Date: 14-7-21
* Time: 下午9:51
*/
public class Demo1 {
/**
* 在jdk1.5之前,我们在往一个集合中存放一个对象或者基本数据类型的时候,取出来的时候,如果数据类型
* 不匹配,那么就不可避免的需要做一些强制类型转换的工作,那么这就不可避免的出现类型转换异常。
*/
public static void main(String[] args) {
List list = new ArrayList();
list.add("aaaa");
//这里我们在取出list集合中的值的时候,如果我们使用了Integer去转换
//这个程序在编码的时候不会报错,但是在运行的时候,就会发生异常,ClassCastException.
//这对于一个大型的程序是有风险的,为了解决这个问题,我们尽量让其在编码期间,程序员就能够去发现这个为问题。
//所以jdk1.5之后出现了泛型 ,详见demo2
Integer integer = (Integer) list.get(0);
}
}
</span></span>
三:Demo2
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
import java.util.ArrayList;
import java.util.List;
/**
* 使用泛型的方式,去解决demo1中可能出现的问题
* User: OF895
* Date: 14-7-21
* Time: 下午9:59
*/
public class Demo2 {
public static void main(String[] args) {
//这里我们限制了使用泛型,所以这个list中只能够存放String类型的参数
//否则在编码期间就会报错
List<String> list = new ArrayList<String>();
list.add("hello world");
}
}
</span></span>
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
/**
* map集合中使用泛型
* User: OF895
* Date: 14-7-21
* Time: 下午10:07
*/
public class Demo3 {
public static void main(String[] args) {
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(1, "aa");
map.put(2, "bb");
map.put(3, "cc");
map.put(4, "dd");
map.put(5, "ee");
/**
*
* 取出map集合中存放的值
*/
Set<Map.Entry<Integer, String>> set = map.entrySet();
Iterator<Entry<Integer, String>> iterator = set.iterator();
while (iterator.hasNext()) {
Entry<Integer, String> entry = iterator.next();
//通过迭代得出的key
Integer key = entry.getKey();
//通过迭代得出的value
String value = entry.getValue();
System.out.println(key + " : " + value);
}
}
}
</span></span>
五:Demo4
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
/**
* User: OF895
* Date: 14-7-23
* Time: 下午11:11
*/
public class Demo4 {
public static void main(String[] args) {
save(new ArrayList<String>());
save(new LinkedList<Integer>());
}
//当我们在设计一个方法的时候,如果不确定方法的调用者,会传入声明类型的参数
//那么我们可以使用“?”这个通配符
//注意点:使用通配符的时候的注意点:
//不能再使用“参数”与“具体类型”相关的方法。
public static void save(Collection<?> c){
//切记:这里就不能这么写了,为什么?
//其实很好理解,因为我们不确定这个方法的调用者,会传入什么类型的参数
//如果这里直接添加的是String类型的,而上面调用save()方法时候,传入的是Integer类型,怎么办?
// c.add("hello world");
}
}
</span></span>
六:泛型声明在“类”上面。(需要先声明,再使用)
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
/**
* 自定义泛型类
* <p/>
* <p/>
* 为什么要将泛型声明在类上面?
* 1 当我们一个类中,多个方法使用到同一种类型的泛型的时候(比如T泛型),避免在每一个方法上面都声明T,那么我们
* 可以考虑将泛型声明放在类上面,这样在方法上就可以不声明了(注意必须是这个类中的方法)
* <p/>
* 2当泛型被声明在类上面的时候,它的作用域仅限于这个类里面。
* <p/>
* 3声明在类上面的方法,只可以在类中的“非静态”方法上面使用,静态方法如果需要使用泛型,那么仍然需要自己去声明,然后再使用。
* <p/>
* User: OF895
* Date: 14-7-22
* Time: 上午12:10
*/
public class GenericClass<T> {
public void methodA(T t) {
System.out.println("这里的泛型T,因为定义在了类上面,所以这个类里面的方法都是可以使用这个泛型的");
}
public <E> void methodB(E e) {
System.out.println("这里的泛型E,因为没有在类上面声明,所以在使用之前需要在方法上先声明");
}
public static <T> T methodC(T t) {
System.out.println("在泛型类上面声明的泛型变量,是不可以直接使用在“静态方法”上面的,需要我们自己在方法上面去声明");
return null;
}
}
</span></span>
七:泛型声明在“方法”上
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
/**
* 泛型方法的使用(自定义泛型方法)
* User: OF895
* Date: 14-7-21
* Time: 下午11:26
*/
public class GenericMethod {
/**
* 自定义泛型方法
* <T> 这个代表声明T,在java中,任何变量使用之前,都需要先声明,然后才可以使用,这是一种语法。
* <p/>
* T:代表的是方法methodA的返回值类型是T,也就意味着我们在参数T中传入声明类型,那么返回值就是什么类型。
*/
public <T> T methodA(T t) {
T obj;
obj = t;
return obj;
}
public <T, E, F> void methodB(T t, E e, F f) {
System.out.println("在自定义泛型方法中,我们是可以定义多个泛型参数的,只需要在使用之前都声明一下就可以了");
}
}
</span></span>
八:自定义“泛型”方法的一个例子(自定义一个reverse的方法,接受任意类型的数组,数组的元素顺序颠倒)
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.generic;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 自定义一个reverse的方法,接受任意类型的数组,数组的元素顺序颠倒。
* User: OF895
* Date: 14-7-21
* Time: 下午11:56
*/
class ReverseArray {
public <T> void reverse(T[] t){
List list = Arrays.asList(t);
Collections.reverse(list);
for(Object i: list){
System.out.println(i);
}
}
}
</span></span>
测试上面的代码:
<span style="font-size:18px;"><span style="font-size:18px;">public class Test {
public static void main(String[] args) {
String[] arr = new String[]{"a","b","c","d","e"};
Integer[] arrInt = new Integer[]{1,2,3,4,5,6};
ReverseArray reverseArray = new ReverseArray();
reverseArray.reverse(arrInt);
}
}
</span></span>
九:“增删改查”的一个“基类”(使用泛型的好例子)
JavaBean:
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;
/**
* User: OF895
* Date: 14-7-23
* Time: 下午10:01
*/
//“种类”的javabean
public class Category {
}
</span></span>
BaseDao:
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;
import org.hibernate.Session;
/**
*
* 一个增删改查的基类
* User: OF895
* Date: 14-7-23
* Time: 下午9:24
*/
public class BaseDao<T> {
private Session session;
private Class clazz;
//如果某个子类实现了BaseDao,那么就调用一下这个构造方法
//掺入具体子类的class对象
public BaseDao(Class clazz) {
this.clazz = clazz;
}
//增加
public void add(T t){
session.save(t);
}
//查找
public T find(String id){
//1在hibernate中,要查找某个对象,是根据传入的id,来作为条件的
//但是这里有个问题,在查找获得这个对象的时候,我们需要传入“被查找”对象的class,但这个是无法通过泛型T,t.getClass或者t.class获得,泛型不能这么使用
//那么如何解决这个问题?
//方法:使用构造方法,由使用这个类的子类去传入。
return (T) session.get(clazz,id);
}
//更新
public void update(T t){
session.update(t);
}
//删除
public void delete(String id){
//在hibernate框架中,要实现删除,需要两步走
//1首先根据传入的id,“查找”出这个对象
//2然后再“删除”这个对象。
T t = (T) session.get(clazz,id);
session.delete(t);
}
}
</span></span>
CategoryDao:(这里在)
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.advance;
/**
* User: OF895
* Date: 14-7-23
* Time: 下午10:01
*/
/**
*
* 这里这个CategoryDao只要继承了BaseDao,那么它就拥有了“增删改查”的方法,这也是面向对象的设计方法
*/
public class CategoryDao extends BaseDao<Category> {
//因为我们在使用BaseDao中的“增删改查”的方法的时候,需要传入具体子类的class类型,指明是那个具体的子类。
//所以这里我们通过子类调用父类的构造函数的方式去传入。
//思考:这样虽然可以实现不同对象都调用BaseDao的方法,但是代码不够优雅。需要显示调用父类的构造方法,传入字节码文件,有更优雅的实现方式。
public CategoryDao() {
super(CategoryDao.class);
}
}
</span></span>
十:
javabean:
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;
/**
* User: OF895
* Date: 14-7-23
* Time: 下午10:33
*/
public class Book {
}
</span></span>
NewBaseDao:(这里优化了例子9中的代码,提高了代码的优雅性,我们不需要在子类中的构造方法中传入类型了)
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;
import org.hibernate.Session;
import java.lang.reflect.ParameterizedType;
/**
* 一个基类:实现增删改查
* User: OF895
* Date: 14-7-23
* Time: 下午10:20
*/
public class NewBaseDao<T> {
//org.hibernate.Session,通过这个类可以使用“增删改查”
private Session session;
private Class clazz;
public NewBaseDao() {
Class cla = this.getClass();//这里首先要明白一点:当子类实现这个父类的时候,在子类new对象的时候,会隐式的调用父类的“无参”构造方法,那么这里this指的就是子类,所以这里的class也是“子类”的class.
ParameterizedType parameterizedType = (ParameterizedType) cla.getGenericSuperclass();//这个方法获得的就是父类的“泛型类型”,类似于BaseDao<Category>,或者BaseDao<Book>,这个后面的参数就是由具体的子类去决定的。
/**
* 不能写成: clazz = parameterizedType.getActualTypeArguments()[0].getClass;
*
* 这里为什么要写成parameterizedType.getActualTypeArguments()[0]?
*
* 因为 NewBaseDao<Category,Book>,这个后面的实际的泛型对象,可能有多个,我们需要显示指明具体的哪一个泛型。
*/
//通过这样的构造方法,那么我们子类在实现这个NewBaseDao的时候,就不需要通过构造函数参入class了,因为父类,会自己通过反射去查找,这样代码更加优雅。
clazz = (Class) parameterizedType.getActualTypeArguments()[0];
System.out.println(clazz);
}
//增加
public void add(T t) {
session.save(t);
}
//查找
public T find(String id) {
//1在hibernate中,要查找某个对象,是根据传入的id,来作为条件的
//但是这里有个问题,在查找获得这个对象的时候,我们需要传入“被查找”对象的class,但这个是无法通过泛型T,t.getClass或者t.class获得,泛型不能这么使用
//那么如何解决这个问题?
//方法1:使用构造方法,由使用这个类的子类去传入。
//方法2:当某个“子类”去实现这个“基类”的时候,由基类自己去获得子类的class类型
return (T) session.get(clazz, id);
}
//更新
public void update(T t) {
session.update(t);
}
//删除
public void delete(String id) {
//在hibernate框架中,要实现删除,需要两步走
//1首先根据传入的id,“查找”出这个对象
//2然后再“删除”这个对象。
T t = (T) session.get(clazz, id);
session.delete(t);
}
}
</span></span>
BookDao:
<span style="font-size:18px;"><span style="font-size:18px;">package cn.itcast.newAdvance;
/**
* User: OF895
* Date: 14-7-23
* Time: 下午10:34
*/
public class BookDao extends NewBaseDao<Book> {
public static void main(String[] args) {
//在BaseDao中,做了操作后,这里在new子类对象的时候,就会默认调用“父类”构造方法了
BookDao bookDao = new BookDao();
}
}</span></span>