`
小果果
  • 浏览: 117401 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

java泛型

    博客分类:
  • Java
阅读更多
java泛型
 泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。
  Java语言引入泛型的好处是安全简单。
  在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。
  泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,提高代码的重用率。
  泛型在使用中还有一些规则和限制:
  1、泛型的类型参数只能是类类型(包括自定义类),不能是简单类型。
  2、同一种泛型可以对应多个版本(因为参数类型是不确定的),不同版本的泛型类实例是不兼容的。
  3、泛型的类型参数可以有多个。
  4、泛型的参数类型可以使用extends语句,例如<T extends superclass>。习惯上成为“有界类型”。
  5、泛型的参数类型还可以是通配符类型。例如Class<?> classType = Class.forName(java.lang.String);
  泛型还有接口、方法等等,内容很多,需要花费一番功夫才能理解掌握并熟练应用。在此给出我曾经了解泛型时候写出的两个例子(根据看的印象写的),实现同样的功能,一个使用了泛型,一个没有使用,通过对比,可以很快学会泛型的应用,学会这个基本上学会了泛型70%的内容。
Java代码
例子一:使用了泛型  
  public class Gen<T> {  
  private T ob; //定义泛型成员变量  
  public Gen(T ob) {  
  this.ob = ob;  
  }  
  public T getOb() {  
  return ob;  
  }  
  public void setOb(T ob) {  
  this.ob = ob;  
  }  
  public void showTyep() {  
  System.out.println("T的实际类型是: " + ob.getClass().getName());  
  }  
  }  
  public class GenDemo {  
  public static void main(String[] args){  
  //定义泛型类Gen的一个Integer版本  
  Gen<Integer> intOb=new Gen<Integer>(88);  
  intOb.showTyep();  
  int i= intOb.getOb();  
  System.out.println("value= " + i);  
  System.out.println("----------------------------------");  
  //定义泛型类Gen的一个String版本  
  Gen<String> strOb=new Gen<String>("Hello Gen!");  
  strOb.showTyep();  
  String s=strOb.getOb();  
  System.out.println("value= " + s);  
  }  
  }   
  例子二:没有使用泛型  
  public class Gen2 {  
  private Object ob; //定义一个通用类型成员  
  public Gen2(Object ob) {  
  this.ob = ob;  
  }  
  public Object getOb() {  
  return ob;  
  }  
  public void setOb(Object ob) {  
  this.ob = ob;  
  }  
  public void showTyep() {  
  System.out.println("T的实际类型是: " + ob.getClass().getName());  
  }  
  }  
  public class GenDemo2 {  
  public static void main(String[] args) {  
  //定义类Gen2的一个Integer版本  
  Gen2 intOb = new Gen2(new Integer(88));  
  intOb.showTyep();  
  int i = (Integer) intOb.getOb();  
  System.out.println("value= " + i);  
  System.out.println("----------------------------------");  
  //定义类Gen2的一个String版本  
  Gen2 strOb = new Gen2("Hello Gen!");  
  strOb.showTyep();  


  类型参数通配符

  假设我们需要写一个方法来显示一个List中的元素。[3]在以前,我们只需要象这样写段代码:


public static void printList(PrintWriter out, List list) {    for(int i=0, n=list.size(); i < n; i++) {        if (i > 0) out.print(", ");        out.print(list.get(i).toString());    }}在Java5.0中,List是一个泛型类型,如果我们试图编译这个方法,我们将会得到unchecked警告。为了解决这些警告,您可能需要这样来修改这个方法:public static void printList(PrintWriter out, List<Object> list) {    for(int i=0, n=list.size(); i < n; i++) {        if (i > 0) out.print(", ");        out.print(list.get(i).toString());    }}  这段代码能够编译通过同时不会有警告,但是它并不是非常地有效,因为只有那些被声明为List<Object>的list才会被允许使用这个方法。还记得么,类似于List<String>和List<Integer>这样的List并不能被转型为List<Object>。事实上我们需要一个类型安全的printList()方法,它能够接受我们传入的任何List,而不关心它被参数化为什么。解决办法是使用类型参数通配符。方法可以被修改成这样:


public static void printList(PrintWriter out, List<?> list) {    for(int i=0, n=list.size(); i < n; i++) {        if (i > 0) out.print(", ");        Object o = list.get(i);        out.print(o.toString());    }}  这个版本的方法能够被编译过,没有警告,而且能够在任何我们希望使用的地方使用。通配符“?”表示一个未知类型,类型List<?>被读作“List of unknown”
作为一般原则,如果类型是泛型的,同时您并不知道或者并不关心值的类型,您应该使用“?”通配符来代替一个未经处理的类型。未经处理的类型被允许仅是为了向下兼容,而且应该只能够被允许出现在老的代码中。注意,无论如何,您不能在调用构造器时使用通配符。下面的代码是非法的:

List<?> l = new ArrayList<?>();

  创建一个不知道类型的List是毫无道理的。如果您创建了它,那么您必须知道它将保持的元素是什么类型的。您可以在随后的方法中不关心元素类型而去遍历这里list,但是您需要在您创建它的时候描述元素的类型。如果你确实需要一个List来保持任何类型,那么您只能这么写:


List<Object> l = new ArrayList<Object>();  从上面的printList()例子中,必须要搞清楚List<?>既不是List<Object>也不是一个未经处理的List。一个使用通配符的List<?>有两个重要的特性。第一,考察类似于get()的方法,他们被声明返回一个值,这个值的类型是类型参数中指定的。在这个例子中,类型是“unknown”,所以这些方法返回一个Object。既然我们期望的是调用这个object的toString()方法,程序能够很好的满足我们的意愿。

  第二,考察List的类似add()的方法,他们被声明为接受一个参数,这个参数被类型参数所定义。出人意料的是,当类型参数是未确定的,编译器不允许您调用任何有不确定参数类型的方法——因为它不能确认您传入了一个恰当的值。一个List(?)实际上是只读的——既然编译器不允许我们调用类似于add(),set(),addAll()这类的方法。

  界定通配符

  让我们在我们原来的例子上作些小小的稍微复杂一点的改动。假设我们希望写一个sumList()方法来计算list中Number类型的值的合计。在以前,我们使用未经处理的List,但是我们不想放弃类型安全,同时不得不处理来自编译器的unchecked警告。或者我们可以使用List<Number>,那样的话我们就不能调用List<Integer>、List<Double>中的方法了,而事实上我们需要调用。如果我们使用通配符,那么我们实际上不能得到我们期望的类型安全,我们不能确定我们的方法被什么样的List所调用,Number?还是Number的子类?甚至,String?这样的一个方法也许会被写成这样:


public static double sumList(List<?> list) {    double total = 0.0;    for(Object o : list) {        Number n = (Number) o;  // A cast is required and may fail        total += n.doubleValue();    }    return total;}  要修改这个方法让它变得真正的类型安全,我们需要使用界定通配符(bounded wildcard),能够确保List的类型参数是未知的,但又是Number或者Number的子类。下面的代码才是我们想要的:


public static double sumList(List<? extends Number> list) {    double total = 0.0;    for(Number n : list) total += n.doubleValue();    return total;}  类型List<? extends Number>可以被理解为“Number未知子类的List”。理解这点非常重要,在这段文字中,Number被认为是其自身的子类。

  注意,这样的话,那些类型转换已经不再需要了。我们并不知道list中元素的具体类型,但是我们知道他们能够向上转型为Number,因此我们可以把他们从list中把他们当作一个Number对象取出。使用一个for/in循环能够稍微封装一下从list中取出元素的过程。普遍性的原则是当您使用一个界定通配符时,类似于List中的get()方法的那些方法将返回一个类型为上界的值。因此如果我们在for/in循环中调用list.get(),我们将得到一个Number。在前一节说到使用通配符时类似于list.add()这种方法中的限制依然有效:举个例子来说,如果编译器允许我们调用这类方法,我们就可以将一个Integer放到一个声明为仅保持Short值的list中去。

  同样可行的是使用下界通配符,不同的是用super替换extends。这个技巧在被调用的方法上有一点不同的作用。在实际应用中,下界通配符要比上界通配符用得少。我们将在后面的章节里讨论这个问题。
  String s = (String) strOb.getOb();  
  System.out.println("value= " + s);  
  }  
  }   
  运行结果:  
  两个例子运行Demo结果是相同的,控制台输出结果如下:  
  T的实际类型是:  
  java.lang.Integer  
  value= 88 
  ----------------------------------  
  T的实际类型是: java.lang.String  
  value= Hello Gen!  
  Process finished with exit code 0   
  看明白这个,以后基本的泛型应用和代码阅读就不成问题了。 
分享到:
评论

相关推荐

    Java泛型编程指南.pdf

    Java泛型编程指南.pdf 此文章译自SUN的泛型编程指南

    Java泛型和集合

    Java Generics and Collections 英文版,详细描述java 泛型技术

    java 泛型类的类型识别示例

    java 泛型类的类型识别示例 java 泛型类的类型识别示例 java 泛型类的类型识别示例

    java 泛型接口示例

    java 泛型接口示例 java 泛型接口示例 java 泛型接口示例

    Java泛型的用法及T.class的获取过程解析

    主要介绍了Java泛型的用法及T.class的获取过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下

    java 泛型方法使用示例

    java 泛型方法使用示例 java 泛型方法使用示例 java 泛型方法使用示例

    JAVA泛型加减乘除

    这是一个使用JAVA实现的泛型编程,分为两部分,第一部分创建泛型类,并实例化泛型对象,得出相加结果。 第二部分用户自行输入0--4,选择要进行的加减乘除运算或退出,再输入要进行运算的两个数,并返回运算结果及...

    java泛型技术之发展

    java泛型技术之发展,学习JAVA 泛型的不错东东

    1.java泛型定义.zip

    1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1.java泛型定义.zip1....

    很好的Java泛型的总结

    很好的Java泛型的总结,看完之后你一定会知道java泛型的底层机制,你一定会学会Java泛型!

    4.java泛型的限制.zip

    4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip4.java泛型的限制.zip...

    java泛型学习ppt

    java,学习java泛型,java培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptxjava培训之泛型.pptx

    java泛型总结

    深入理解java泛型,包括类名泛型的定义,方法泛型定义,泛型的返回

    SUN公司Java泛型编程文档

    Sun公司的Java泛型编程文档,英文原版和网络翻译版,想对泛型有更清楚的认识的朋友可以看看,必定会有所帮助

    java泛型详解.pdf

    java泛型详解.pdf

    JAVA泛型简单排序实例

    JAVA泛型源代码实现以下功能:返回数组元素的最大值/最小值下标;判断数组元素是否按升序排列;T对象数组排序;二分法查找key元素;

    思维导图之Java泛型详解

    思维导图之Java泛型详解

    Java泛型技术之发展

    Java泛型技术之发展

    JAVA泛型教程(帮你解决学习泛型的苦恼)

    JAVA泛型教程(帮你解决学习泛型的苦恼). Java 泛型编程可能会碰到很多问题,本教程可能会对你有帮助哦。

Global site tag (gtag.js) - Google Analytics