- 什么是泛型?
上面的官方解释有点太官方,换成自己的话我觉得就是2点:
1,解决元素存储的安全性问题
2,解决获取数据元素时,不需要类型强转。具体代码如下:
package tz.web.main;
import java.util.List;
import com.google.common.collect.Lists;
/**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 没有使用泛型
*/
public class Linkin
{
public static void main(String[] args)
{
//@SuppressWarnings("rawtypes") 这个压制警告,就是说原始的数据结构类型,也就是说没有加入泛型检查
//首先这里数据不安全,我本来是要想放字符串进集合的,结果不小心放了数字了,丫的编译不会有问题的
List list = Lists.newArrayList();
list.add("1");
list.add(2);
//在下面的代码
for (Object object : list)
{
//不加入泛型,下面的代码的类型默认都是object的,所以在使用的时候,一般都会强转
String str = (String) object;
//java.lang.Integer incompatible with java.lang.String
//在编译的时候,也是没有问题的,但是实际运行的时候,发生问题了,类型转换错误。除非你每次都来判断具体的类型:object instanceof String
System.out.println(str);
}
}
}
package tz.web.main;
import java.util.List;
import com.google.common.collect.Lists;
/**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 使用了泛型
*/
public class Linkin
{
public static void main(String[] args)
{
List<String> list = Lists.newArrayList();
list.add("1");
//你要是这里放入list中的对象的类型不对的话,编译就不通过
//list.add(2);
//在迭代循环list的时候,也不需要自己来每次强转类型了
for (String string : list)
{
System.out.println(string);
}
}
}
总结:使用泛型的优势:
1,类型安全,使编译器对泛型定义的类型做判断限制.如保证TreeSet里的元素类型必须一致
2,消除强制类型的转换,如,使用Comparable比较时每次都需要类型强转。
- 泛型类
声明带泛型的类:
class 类名<泛型类型1,泛型类型2……>{
泛型类型 变量名;
泛型类型 方法名(){}
返回值类型 方法名(泛型类型 变量名){}
}
使用带泛型的类:
类名<具体类> 对象名 = new 类名<具体类>();
类型参数规范:推荐使用规范-常见的泛型,泛型只保存在源文件中,class文件中不存在;也就是说在编译阶段就会丢失,基本数据类型不能作为泛型类型;
K 键,比如映射的键 key的类型
V 值,比如Map的值 value类型
E 元素,比如Set<E> Element表示元素,元素的类型
T 泛型,Type的意思
T只能是类,不能用基本数据类型填充。
关于泛型类,主要注意一下几点:
1.对象实例化时不指定泛型,默认为:Object。
2.泛型不同的引用不能相互赋值。
3.加入集合中的对象类型必须与指定的泛型类型一致。
4.静态方法中不能使用类的泛型。
5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
6.不能在catch中使用泛型
7.从泛型类派生子类,泛型类型需具体化
package tz.web.main;
/**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^ 自定义泛型类
*/
public class Linkin<T>
{
private T name;
private T andress;
//以下是2个构造器,注意了:在创建带泛型声明的自定义类时,构造器还是原来的构造器,没有变化的话。变化的只是在使用的过程中,为泛型形参传入实际的类型参数。
public Linkin()
{
}
public Linkin(T name, T andress)
{
this.name = name;
this.andress = andress;
}
public T getName()
{
return name;
}
public void setName(T name)
{
this.name = name;
}
public T getAndress()
{
return andress;
}
public void setAndress(T andress)
{
this.andress = andress;
}<p><span style="color:blue;"><span style="white-space:pre"> </span>//</span><span style="color:blue;">static</span><span style="color:blue;">的方法中不能声明泛型</span></p><p><span style="color:#C00000;"><span style="white-space:pre"> </span>//public </span><span style="color:#C00000;">static void show(T t){</span></p><p><span style="color:#C00000;"><span style="white-space:pre"> </span>//}</span></p>
public static void main(String[] args)
{
Linkin<String> linkin = new Linkin<String>();
System.out.println(linkin.getAndress());
}
}
若一个类中多个字段需要不同的泛型声明,则在声明类的时候指定多个泛型类型即可。
public interface Map<K,V> {
Set<Map.Entry<K, V>> entrySet();
}
- 通配符
List<?> 表示未知元素的List集合;这种带通配符的List仅表示各种泛型List的父类,并不能把元素添加入集合中。
package tz.web.main;
import java.util.ArrayList;
import java.util.List;
/**
*
* @version 1L
* @author LinkinPark
* @since 2014-12-7
* @motto 梦似烟花心似水,同学少年不言情
* @desc ^编译器无法基于信息作类型推断
*/
public class Linkin
{
//表示可接受任意类型的List集合
public void show(List<?> list)
{
}
public static void main(String[] args)
{
//这种带通配符的List仅仅表示他是各种泛型List的父类,并不能把元素添加到其中。
List<?> list = new ArrayList<Linkin>();
//编译错误 因为上面使用了通配符,这个list就不知道往他里面添加的是什么类型的对象,所以就不能丢进去
//list.add("111");
//这里编译通过,null是唯一的例外。
list.add(null);
}
}
- 泛型的上限与下限
声明对象:类名<? extends 类> 对象名
定义类:类名<泛型标签 extends 类>{}
注意了:与类同时继承父类,实现接口相似:为类型形参指定多个类型的时候,所有的接口上线必须位于类上限之后。
设置泛型对象的下限使用super,表示参数类型只能是该类型或该类型的父类:
声明对象:类名<? super 类> 对象名称
定义类:类名<泛型标签 extends类>{}
注意了:不能同时设置上限和下限
package tz.web.main;
public class Linkin<T extends Number>
{
public static void main(String[] args)
{
//传入的实际类型比如是Number的父类的子类,或者是接口的实现类。
Linkin<Integer> linkin1 = new Linkin<Integer>();
Linkin<Long> linkin2 = new Linkin<Long>();
//Bound mismatch: The type String is not a valid substitute for the bounded parameter <T extends Number> of the type Linkin<T>
//Linkin<String> linkin3 = new Linkin<String>();
}
}
- 泛型接口
泛型接口子类有两种方式:1,直接在子类后申明泛型;2,在子类实现的接口中给出具体的泛型类型
public class DaoImpl<T> implements IDAO<T>{
}
推荐使用这种方式,一般都是定义一个泛型接口,然后里面每个具体的实现类的类型都基本确定下来了。上面的那种情况里面的实现类其实也是泛型的,一般不会写这种情况的代码。
public class DaoImpl implements IDAO<String> {
}
- 泛型方法
格式:
<泛型标签> 返回值类型 方法名([泛型标签 参数]...)
public static <T> T show(T param){
return param;
}
package tz.web.main;
public class Linkin
{
public static <T> T show(T param){
return param;
}
public static void main(String[] args)
{
System.out.println(Linkin.show("LinkinPark..."));
}
}
- 泛型方法和类型通配符的区别:
public void test(List<?> list);就可以写成 public <T> void test(List<T> list)
1,如果方法中的类型形参只使用了一次,类型形参的唯一效果就是可以在不同的调入点传入不同的实际类型,那么这个时候应该使用通配符,通配符就是被设计用来支持灵活子类的。如果方法中的类型形参表示一个或者多个参数之间的依赖关系,或者是说表示方法的返回值和方法参数之间的关系,就应该使用泛型方法。简单的说:使用一次,用通配符,使用了好多次呢,用泛型方法。
2,类型通配符即可以在方法签名中定义形参的类型,也可以用于定义变量的类型。但是泛型方法中类型形参必须在对应方法中显式申明。
- 泛型的嵌套
package tz.web.main;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
public class Linkin
{
public static void main(String[] args)
{
Map<String,String> map = new HashMap<String,String>();
map.put("1", "A");
map.put("2", "B");
map.put("3", "C");
map.put("4", "D");
//循环map的2种方式
System.out.println("===============第一种===============");
Set<String> keySet = map.keySet();
for(Iterator<String> it = keySet.iterator();it.hasNext();)
{
String key = it.next();
System.out.println(key + "-->" + map.get(key));
}
System.out.println("===============第二种===============");
Set<Map.Entry<String, String>> set = map.entrySet();
Iterator<Map.Entry<String, String>> it = set.iterator();
while(it.hasNext())
{
Map.Entry<String, String> entry = it.next();
System.out.println(entry.getKey() + "-->" + entry.getValue());
}
}
}
- 泛型的擦除
比如List<String> 类型转换成List,则该List对集合元素的类型检查变成了变量的上限即Object。
package tz.web.main;
import java.util.ArrayList;
import java.util.List;
public class Linkin<T extends Number>
{
private T t;
public Linkin(T t)
{
this.t = t;
}
public T getT()
{
return t;
}
public void setT(T t)
{
this.t = t;
}
public static void main(String[] args)
{
Linkin<Integer> n = new Linkin<Integer>(5);
Integer i1 = n.getT();
Linkin n2 = n;//会丢掉泛型信息
Number num = n2.getT();
//下面这行代码比如要强转了
Integer i2 = (Integer) n2.getT();
List<Integer> intList = new ArrayList<Integer>();
intList.add(1);
//List<String> strList = null;intList = strList;//不能转换
List list = intList;
List<String> strList = list;//不会报错,只有未经检查警告,此时list实际引用的是List<Integer>
//java.lang.Integer incompatible with java.lang.String
System.out.println(strList.get(0));//企图当做String类型对象取出
//下面这行代码和上面演示的效果一模一样
//System.out.println((String) intList.get(0));
}
}
- 泛型和数组
- 泛型开发实例
泛型接口实现类 GenericDAOImpl 实现泛型接口里的所有抽象方法
public interface IGenericDAO<T> {
T get(Serializable id);
T save(T newInstance);
void remove(Serializable id);
void update(T object);
}
public class GenericDAOImpl<T> implements IGenericDAO<T>{
public T get(Serializable id) {
return null;
}
public T save(T newInstance) {
return null;
}
public void remove(Serializable id) {
}
public void update(T object) {
}
public List<T> query() {
return null;
}
}
- 特别需要注意的地方:
2,在敲代码的过程中,只要是没有出现未检查的警告,那么程序在运行的时候肯定是不会发生类型转换异常的。
3,在写泛型的方法时,要是只是泛型的类型参数不同,那么这2个方法就算是同一个方法。因为编译后的泛型就去掉了,所以当然是一个方法
public static void show(List<? extends Number> l){
}
public static void show(List<? super String> l){
}
4,静态方法中不能使用类的泛型。 不能用基本类型实例化泛型类型参数,例如List<int> linkin = null ; 编译报错。
5,泛型和继承的关系:如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!比如:String是Object的子类,但是List<String >并不是List<Object>的子类。关于上面这点,要和数组区分开来:[B] b是[A] a的子类,但是在泛型接口不行的。
6,对象实例化时不指定泛型,默认为:Object。泛型不同的引用不能相互赋值。
7,从泛型类派生子类,泛型类型需具体化。其实泛型最大的用处就是用来定义一个泛型DAO,里面实现基本的CRUD方法,所以在继承的时候就直接传入实际的类型好了。关于泛型dao,在整理完反射后会统一整理。