您的位置:首页 > 职场人生

黑马程序员基础加强 泛型

2014-03-21 11:10 281 查看
 ------- <a href="http://www.itheima.com" target="blank">android培训</a>、<a href="http://www.itheima.com" target="blank">java培训</a>、期待与您交流! ----------

泛型是jdk1.5的所有新特性中最难深入掌握的部分,不过,我们在实际应用中有需要掌握得那么深入,掌握泛型中一些最基本的内容就差不多了。

ArrayList可心存储各种类型的对象,如:

public static void main(String[] args) {

  ArrayList als =new ArrayList();
  als.add(1);

   als.add(3L);

   als.add("abc");
   System.out.println(als.get(2));

}

而ArrayList<String> als = new ArrayList<String>();代表ArrayList只能存储String类型的对象。这是在集合中使用泛型。
在JDK文档中的类名后有“<T>”或“<E>”说明这个类支持泛型
//在反射中使用泛型

//Constructor<String>指示这个构造函数是String类的构造函数,所以用这个构造函数newInstance的对象就是String对象,不需要再进行转换,代码如下:
Constructor<String> constructor = String.class.getConstructor(StringBuffer.class);
String str = constructor.newInstance(new StringBuffer("ddd"));
System.out.println(str);

泛型是提供给javac编译器使用的,可心限定集合中的输入类型,让编译器挡住源程序中的非法输入,编译器编译带类型说明的集合时会去除掉“类型”信息,使程序运行效率不受影响,对于参数化的泛型类型,getClass()方法的返回值和原始类型完全一样。由于编译生成的字节码会去掉泛型的类型信息,只要能跳过编译器,就可心往某个泛型集合中加入其他类型的数据,例如,用反射得到集合,再调用其add方法即可。

在JDK1.5中,你还可以按可以按原来的方式将各种不同类型的数据装到一个集合中,但编译器会报告unchecked警告。

ArrayList<E>类定义和ArrayList<Integer>类引用中涉及如下术语:

整个ArrayList<E>称为泛型类型

ArrayList<E>中的E称为类型变量或类型参数

整个ArrayList<Interger>称为参数化的类型

ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

ArrayList<Integer>中的<>读为typeof

ArrayList称为原始类型

参数化类型与原始类型的兼容性:

参数化类型可以引用一个原始类型的对象,编译报告警告,例如,

Collection<String> c = new Vector();

原始类型可以引用一个参数化类型的对象,编译报告警告,例如,

Collection<String> c = new Vector<String>();

参数化类型不考虑类型参数的继承关系:

Vector<String> v = new Vector<Object>();//错误!
Vector<Object> v = new Vector<String>();//也错误!

假设Vector<String> v = new Vector<Object>();可以的话,那么以后从v中取出的对象当作String用,而V实际指向的对象中可以机器翻译任意的类型对象;

假设Vector<Object> v = new Vector<String>();可以的话,那么以后可以向v中加入任意的类型对象,而v实际指向的集合中只能装String类型的对象。

在创建数组实例时,数组的元素不能使用参数化的类型,例如,下面语句有错误:

Vector<Integer> VectorList[] = new Vecotr<Integer>[10];

思考下面的代码会报错吗?

Vector v1 = new Vector<String>();

Vector<Object> v = v1;

这是不会报错的,因为编译阶段是一句一句编译的,编译第一句的时候不会报错,接着编译第二句的时候,检查到达把一个参数化类型引用变量指向一个原始类型引用变量,这当然是可以的,所以不会报错。

 

泛型中的?通配符,表示参数化类型可以为任意类型
 返回顶部

实例:设计一个方法,可以打印任意参数化类型的集体对象。

Public static void main(String args[])

{

  Collection<String> collection = newArrayList();

  collection.add("abc");
  printCollection(collection);

}

public static void printCollection(Collection<?> collection)

{

  //collection.add(1);这句代码会报错,比如传进来的是一个参数化类型为String的集体,这是不合理的。

  collection.size(); //这句代码可以通过

  for(Object obj: collection) //打印集合中的每一个对象

  {

    System.out.println(obj);

  }

}

怎么知道collection.size();这个方法是通用的呢?查JDA文档,只要方法的参数里没有<E>即为通用的,即这个方法是跟参数化的类型无关的

总结:使用?通配符可以引用各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法,在查JDK文档时,在参数里如果有<E>则表示这个方法与类的参数化有关。

泛型中的?通配符的扩展

限定通配符的上边界:
正确:Vector<? extends Number> x = new Vector<Integer>();//限定为Number或其子类
错误:Vector<? extends Number> x = new Vector<String>();

限定通配符的下边界:
正确:Vector<? super Integer> x = new Vector<Number>();//限定为Number或父类
正确:Vector<? super Integer> x = new Vector<Byte>();

提示:限定通配符总是包括自己。
泛型集合的综合案例   返回顶部

能写出下面的实例即代表掌握了Java的泛型集合类:
package cn.itcast.day2;

import java.util.*;
public class GegericSimple {
public static void main(String[] args) {

    Map<String,Integer> maps = newHashMap();

    maps.put("张三", 23);

    maps.put("李四", 24);

    maps.put("王五", 25);

//使用迭代打印出Map映射中的值对,但是Map没有实现Iterator接口,所以不能进行迭代,

//可以把这个Map转换成Set集合,Set实现了Iterator接口

    Set<Map.Entry<String,Integer>> entrySet = maps.entrySet();
    for(Map.Entry<String,Integer> entry: entrySet)

    {
      System.out.println(entry.getKey() + ":" + entry.getValue());

    }   

  }
}

键-值对在JSP页面中也经常要对Set或Map集合进行迭代:

<c:forEach items=”${map}” var=”entry”>

${entry.key}:${entry.valu}

</c:froEach>

 

由C++的模板函数引入自定义泛型

如下函数的结构很相似,仅类型不同:


int add(int x , int y) { return x+y ; }

float add(float x , float y) { return x+y ; }

double add(double x , double y) { return x+y ; }
C++用模板函数解决,只写一个通用的方法,它可适应各种类型,示意代码如下:

Template<class T>

T add(T x, T y) { return (T) ( x +y ) }
在Java中自定义泛型:   返回顶部
public class GegericSimple {
  private static <T> T add(T x,T y)

  {
     return null;

  }
  public static void main(String[] args) {
    Integer i =
a1a3
add(2,3);//参数都为Integer,返回Interger
    Number n = add(2,3.0);//参数都为Number,返回Number
    Object o = add(2,"a");//参数都为Object,返回Object

  }

}

自定义泛型应用实例:交换任意类型的数组中任意两个元素的值
public static void main(String[] args) {
  String[] strs = new String[] {"a","b","c"};
  int[] nums = new int[] {1,2,3};
  swap(strs,1,2);

  //swap(nums,1,2);//这句代码会报错,T类型的参数只能是引用变量型的,不能为基本类型

}

public static <T> void swap(T[] t,int i,int j)

{

  T temp = t[i];

  t[j] = t[i];

  t[i] = temp;

}

Java中的泛型类型(或者泛型)类似于C++中的模板。但是这种相似性仅限于表面,Java语言中的泛型基本上完全是在编译器中实现,用于编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码,这种实现技术称为 擦除(erasure)(编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)。这是因为扩展虚拟机指令集来支持泛型被认为是无法接受的,这会为Java厂商升级其JVM造成难以逾越的障碍。所以,Java的泛型采用了可以完全在编译器中实现的擦除方法。

交换数组中的两个元素的位置的泛型方法语法定义如下:

Private static <T extends Exception> sayHello() throws T

{

  Try{}

  catch(Exception e)//这里用(T e)就是错误的

  { throw (T)e;}

}

用于放置泛型的类型参数的尖括号应出现在方法的其他所有修饰符之后和在方法的返回类型之前,也就是紧邻返回值之前。按照惯例,类型参数通常用个单个大写字母表示。

只有引用类型才能作为泛型方法的实际参数,swap(new int[3],3.5);语句会报告编译错误。

除了在应用泛型时可以使用extends限定符,在定义泛型时也可以使用extends限定符,例如,Class.getAnnotation()方法的定义,并且可以用&来指定多个边界,如<V extends Serializable & cloneable> void method(){}

普通方法、构造方法和静态方法中都可以使用泛型。编译器也不允许创建类型变量的数组。

也可以用类型变量表示异常,称为参数化的异常,可以用于方法throws列表中,但是不能用于catch子句中。如:

在泛型中可以同时有多个类型参数,在定义它们的尖括号中用逗号分隔,例如:

public static <K,V> getValue(K key){return map.get(key);}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: