0.泛型知识体系
1.泛型概论
1)泛型概念
JDK1.5出现的技术,是一种安全机制;再直白一点,泛型就是通过<>定义形式参数,专门用于接收具体的引用类型。另外,{}、[]、()、<>已经全部都有用处。
只要类型不确定就用泛型,使用时明确类型。
2)技术由来
集合中可存储任意类型对象,但是如果取出时调用具体对象特有方法时需要进行向下转型,如果存储对象类型不一致,转型过程中就出抛出ClassCastException异常,给程序带来不安全性。JDK1.5之后就出现了解决方案,即泛型技术。如下列代码:
public class GenericTest
{
public static void main(String[] args)
{
List ls = new ArrayList();
ls.add("Tad");
ls.add("Great");
ls.add(23);
Iterator it = ls.iterator();
while(it.hasNext()){
String str = (String)it.next();//执行到第3次时,出现问题
System.out.println(str.length());
}
}
}
解决方案就是泛型,在定义容器时完成类型确定,如果类型不匹配就会发生编译错误。
3)泛型关键词
(1)ArrayList:原始类型。
(2)ArrayList<E>:泛型类型;<E>:类型参数。
(3)ArrayList<String>:参数类型;String:实际类型参数。
4)泛型优点
(1)将运行时期的ClassCastException转移到编译时期进行检查并以编译失败体现,利于程序员解决问题,提高安全性。
(2)简化书写,避免类型强制转换,提高效率。
2.通配符与类型限定
1)通配符
构造参数化类型时,如果类型参数不确定,就写类型通配符?。
当操作的不同容器中的类型都不确定的时候,而且使用的都是元素从Object类中继承的方法,这时泛型就用通配符?表示。
public class GenericAdv
{
public static void main(String[] args)
{
ArrayList<String> al1= new ArrayList<String>();
al1.add("tad1");
al1.add("tad2");
al1.add("tad3");
printColl(al1);
ArrayList<Integer> al2 = new ArrayList<Integer>();
al2.add(23);
al2.add(23);
al2.add(23);
printColl(al2);
}
public static void printColl(ArrayList<?> al){
Iterator<?> it = al.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
public static void printColl(Collection<?> al){}
通配符应用的具体体现。
2)通配符限定
明确具体类型代表一个类型,明确?代表类型,能不能限定只操作某个范围类型?? extends E 接收E类型或者E的子类型,上限,可以使用E的所有方法:添加元素时用到,如Collection<? extends Person>。
? super E 接收E类型或E的父类型,从容器中取出元素,取出来的都能接收。
?相当于 ? extends Object,所以只能使用Object的方法。
3)类型参数上限与带有接口上限
public class Apple<T extends Number>{
}
public class Apple<T extends Apple && java.io.Serializable>{
}
3.定义及使用泛型
1)定义及使用泛型接口
两种使用方法(1)实现接口时,明确具体的类。
(2)实现接口时,也不明确具体的类。
//定义泛型接口
interface Shower<G>{//声明是泛型类型,类型参数为G
public abstract void show(G g);
}
//实现泛型接口时明确具体类型
class ShowerImplA implements Shower<Integer>{
public void show(Integer g){
System.out.println(g);
}
}
//实现接口时不明确具体类型,泛型类+泛型接口
class ShowerImplB<C> implements Shower<C>{
public void show(C g)
{
System.out.println(g);
}
}
public class GenericInterface
{
public static void main(String[] args)
{
ShowerImplA sib = new ShowerImplA();
sib.show(23);
//sib.show("Tad is a great person in the world!");
ShowerImplB<String> sia = new ShowerImplB<String>();
sia.show("abc");
}
}
2)泛型类
(0)早期做法(自定义泛型类)
package tad.blog.generic;
class Teacher{
}
class Student{
}
class ObjectTools{
private Object obj;
public Object getObj()
{
return obj;
}
public void setObj(Object obj)
{
this.obj = obj;
}
}
public class LongAgo
{
public static void main(String[] args)
{
ObjectTools ot = new ObjectTools();
ot.setObj(new Teacher());
Student stu = (Student)ot.getObj();
}
}
(1)泛型类声明及使用
class Generic<T>//声明是泛型,并且类型参数为T
{
public void info(){
System.out.println("Hello world");
}
}
public class GenericTest
{
public static void main(String[] args)
{
Generic<String> g = new Generic<String>();//泛型类实例时指明具体类型
g.info();
}
}
(2)泛型类型派生类
3)泛型方法
public class GenericMethod
{
public static void main(String[] args)
{
Tools<String> tool = new Tools<String>();
tool.println("Tad is a great person in the world.");
//tool.println(new Integer(23));//不能打印其它类,如果想要做到,就必须重新new对象。
Tools<Integer> tool2 = new Tools<Integer>();
tool2.println(new Integer(23));
//tool2.println("Tad is a great person in the world.");//同样不可以
}
}
class Tools<E>{
public void println(E e){
System.out.println(e.toString());
}
}
如果想要不重新new对象就改变可以使方法打印各种各样的数据,就需要用到方法的泛型。
方法的操作类型不确定,但不一定和调用该方法的对象指定的类型一致。
泛型定义在类上与泛型定义在方法上,作用域是不一样的。定义的位置不同。
方法泛型参数的定义位置是返回值前方修饰符后方。
public class GenericMethod
{
public static void main(String[] args)
{
Tools<String> tools = new Tools<String>();
tools.rawPrintln("Tad is a great person in the world!");
//tools.rawPrintln(new Integer(23));//编译错误
tools.println("Tad is a great person in the world");//要求传入新的类型参数。
tools.println(new Integer(23));//也是ok的
}
}
class Tools<E>{
public void rawPrintln(E e){
System.out.println(e.toString());
}
public <T> void println(T t){
System.out.println(t.toString());
}
}
泛型定义小插曲
(1)泛型类型不能访问类上定义的泛型,如果需要泛型只能定义在方法上。因为类上泛型用到时是在类进行实例化时,而静态方法的调用是不用实例化。
4)类库中的泛型
只要在使用类或或者接口时,该类或接口在API文档描述中带着<>就需要使用,写代码时直接加上,而不是写完再加上, 文档中会说明。保证两边一致。
ArrayList<Object> al = new ArrayList<String>();
ArrayList<String> al = new ArrayList<Object>();
5.泛型擦除和转换
泛型是编译器上的技术,编译时对用指定类型对代码检查,检查不通过,编译失败,检查通过完成之后生成class文件中没有泛型,此就是泛型擦除。运行时可根据具体元素对象获取其具体类型,并用该类型对元素进行转换。