- 什么是泛型?
上面的官方解释有点太官方,换成自己的话我觉得就是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,在整理完反射后会统一整理。