`

java泛型

    博客分类:
  • Java
阅读更多

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。

在使用泛型的版本中并没有减少键盘录入;实际上,比使用强制类型转换的版本需要做更多键入。使用泛型只是带来了附加的类型安全。因为编译器知道关于您将放进Map中的键和值的类型的更多信息,所以类型检查从执行时挪到了编译时,这会提高可靠性并加快开发速度。

对于没有使用泛型处理的代码,在JDK5中,系统会出现编译警告,提示代码为”raw type”。 这是兼容的,如果不想看到这个信息,可以做如下处理:
1, 使用 –source 1.4 参数编译。
2, 使用@SupressWarnings(“unchecked”) annotaion
3, 升级为使用泛型

如果你声明一个泛型List<atype>,那么List的实现中,atype可以是如下类型:
1, atype的实例
2, atype子类的实例(如果atype是一个class)
3, atype的实现类的实例(如果atype是一个接口)

泛型类在多个方法签名间实施类型约束。在List<V>中,类型参数V出现在get()、add()、contains()等方法的签名中。当创建一个Map<K, V>类型的变量时,您就在方法之间声明了一个类型约束。您传递给add()的值将与get()返回的值的类型相同。 类似地,之所以声明泛型方法,一般是因为您想要在该方法的多个参数之间声明一个类型约束。例如,下面代码中的ifThenElse()方法,根据它的第一个参数的布尔值,它将返回第二个或第三个参数,这就做到了参数类型与返回参数类型的一致性:

public <T> T ifThenElse(boolean b, T first, T second) {
     return b ? first : second;
} 


为什么您选择使用泛型方法,而不是将类型T添加到类定义呢?(至少)有两种情况应该这样做:
* 当泛型方法是静态的时,这种情况下不能使用类类型参数(泛型类)。
* 当 T 上的类型约束对于方法真正是局部的时,这意味着没有另一个方法签名中使用相同 类型 T 的约束。泛型方法的类型参数对于方法是局部的,可以简化封闭类型的签名。

对于常见的泛型模式,推荐的名称是:
* K —— 键,比如映射的键。
* V —— 值,比如 List 和 Set 的内容,或者 Map 中的值。
* E —— 异常类。
* T —— 泛型。

比如现在有三个类,Shape类以及两个子类:Circle、Rectangle。

        Circle c = new Circle();
        List<Shape> s1 = new ArrayList<Shape>();//正确
        s1.add(c);//正确
        List<Shape> s2 = new ArrayList<Circle>();//编译错误
        List<? extends Shape> s3 = new ArrayList<Shape>();//正确
        List<? extends Shape> s4 = new ArrayList<Circle>();//正确
        s3.add(c);//编译错误
        s4.add(c);//编译错误

 

泛型和Java对象一样,都有自己的类型,下面两个就是完全独立,没有关系的:

List<Object> list1 = new ArrayList<Object>();
List<String> list2 = new ArrayList<String>(); 

虽然String是Object的子类,但是List<String>可不是List<Object>的子类。

List<String>不是List<Object>的子类,不是String与Object的关系,就是说List<String>不隶属于list<Object>,他们不是继承关系,所以是不行的,这里的extends是表示限制的。所以下面代码第2行是错误的:

List<String> ls = new ArrayList<String>(); //1
List<Object> lo = ls; //2

 
List<?>是任何泛型List的父类型,所以您完全可以将List<Object>、List<Integer>或List<List<List<Flutzpah>>>赋值给它。但这样声明就不对了: List<?> myList = new ArrayList<?>();

List<?>中的元素类型是未知的、不确定的,我们不能向其中添加任何确定的类型对象。因为不知道那是什么类型,所以我们无法传任何东西进去。但唯一的例外是null,它能代表所有类型(即是这种类型也是那种类型),但从List<?>泛型集合中取出的元素类型为Object。

Collection<?> c = new ArrayList<String>();
c.add(null);//可以放进去
c.add(new Object()); // 编译器不能对Collection<?>的类型参数作出足够严密的推理,以确定将Object传递给c.add()是类型安全的。所以编译器将不允许您这么做
for (Object o : c){//取出的元素类型为Object
    System.out.println(o);
}

 

JDK5推出upper bounded ,格式如下:
GenericType <? extends uppderBoundedType>
反之,JDK5还有一个lower bounded,List<? super Integer>可以接收Integer或者它的父类。 <? super T> 语法将泛型类限制为所有T的超类(包括T自身),但只能用于方法的参数中,不可以在返回值用与泛型类中。如果不加以限定,假设某个函数头为<? super Manager> get(),由于编译器不知道该方法究竟会返回什么类,这样就只能用Object类来接收了,这样的话不就又回到泛型以前了吗? 所以这是不允许的。

class C<T extends Comparable<? super T> & Serializable>
我们来分析以下这句,T extends Comparable这个是对上限的限制(意思是不超过它,但可在等于它),Comparable< super T>这个是下限的限制,Serializable是第2个上限。一个指定的类型参数可以具有一个或多个上限。具有多重限制的类型参数可以用于访问它的每个限制的方法和域。

不能实例化泛型类型的数组(new List<String>[3] 是不合法的),除非类型参数是一个未绑定的通配符(new List<?>[3] 是合法的)

JDK 1.5 还允许将「不被method实际用到」的类型参数以符号 '?' 表示,例如:

public static List<?> gMethod (List<?> list){
    return list; // 本例简单原封不动的返回
}

此例gMethod()接受一个List(无论元素的类型是什么),返回一个List(无论其元素类型是什么)。由于不存在(或说不在乎)参数类型(因为method 內根本不去用它),也就不必如平常一样在返回类型之前写出<T>来告知编译器了。上面这个例子无法真正表现出符号'?'的用途,真正的好例子請看JDK1.5 的java.util.Collections 的源码
1.4:

public static Object max(Collection coll) {
    Iterator i = coll.iterator();
    Comparable candidate = (Comparable)(i.next());
    while(i.hasNext()) {
        Comparable next = (Comparable)(i.next());
        if (next.compareTo(candidate) > 0)
        candidate = next;
    }
    return candidate;
}

1.5:

public static <T extends③ Object & Comparable④<? super⑤ T>> T⑥ max(Collection①<? extends② T> coll) {

    Iterator<? extends T> i = coll.iterator();
    T candidate = i.next();
    while(i.hasNext()) {
        T next = i.next();
        if (next.compareTo(candidate) > 0)
        candidate = next;
    }
    return candidate;
}

1. max()接收一个Collection object。
2. 该Collection object 所含元素必須是T-derived object。
3. T 必須继承Object(这倒不必明说,因为Java 必定如此)。
4. T 必須实现Comparable接口。
5. Comparable 所比较的对象类型必須是T 的super type。
6. max()返回T object。
针对上面如此怪异的语法,给个实际的例子就清楚多了:

LinkedList<Shape> sList = new LinkedList<Shape>();
...
Shape s = Collections.max(sList);

我们让Collections.max()接受一个LinkedList<Shape>,这是个Collections object(符合条件1),其中每个元素都是Shape-derived objects(符合条件2),因此本例中的T 就是Shape。Shape 的确实继承自Object(符合条件3),並且必須实现Comparable(才能符合條件4),而被比较对象的类型必須是Shape 的super class(才能符合条件5)。max()比較所得之最大值以Shape 表示(符合条件6)——這是合理的,因为不知道比较出來的結果会是Rect 或Circle,但无论如何它们都可以向上转型为Shape。
为了完成上述的条件4 和条件5,先前的Shape 必须修改,使得可比较。也就是说Shape 必须实现Comparable 接口,其中针对compareTo()用上了典型的Template Method 设计模式,再令每一个Shape-derived classes都实现L(),這就大功告成了。

public abstract class Shape implements Comparable<Shape> {
    ...
    public abstract double L(); //计算周长
    public int compareTo(Shape o) { //假设「以周长为比较依据」合理!
        return (this.L() < o.L() ? -1 : (this.L() == o.L() ? 0 : 1));
    }
}


泛型类型的使用限制:
    * 不应在静态上下文环境中引用类型参数
    * 不能用基本类型实例化泛型类型参数
    * 不能在数据类型转换或 instanceof 操作中使用类型参数 new Object() instanceof T
    * 不能在 new 操作中使用类型参数 new T()
    * 不能在类定义的 implements 或 extends 子句中使用类型参数 class Test<T> implements T

/*
 * 编译通不过,方法重复声明,因为ArrayList<String> 与
 * ArrayList<Integer> 为同一类型
 */
public class X{
    void set(ArrayList<String> y)    {
    }   
    void set(ArrayList<Integer> y){
    }
}

ArrayList<String>,ArrayList<Integer>不同于 c++ 中的 template生成多个类 ,java 中仍是一个类 ,只不过生成字节码文件时替换参数类型为指定上限(默认为Object),编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除,另外Java 语言中的泛型不能接受基本类型作为类型参数――它只能接受引用类型。

class Template{
    public static void main(String[] args){
        C<Number> x = new C<Number>();
        C<Integer> y = new C<Integer>();
        x.get();
        //error,因为方法的参数类型已由调用它的实例确/定为Number,
        //所以类型参数不能是Integer,但可以使用通配符参数来解决:get(C<? extends T> t)或这样也行<X extends T> void  get(C<X> t) 
        x.get(y);
    }
}

class C<T extends Number>{
    private T num;
    public void get(C<T> t){
    }

    public void get(){
        //可以直接调用类型参数对象的方法
        System.out.println(num.intValue());
    }
}
 


仅从某个结构中获取值时使用extends 通配符;仅将值放入某个结构时使用 super 通配符;同时执行以上两种操作时不要使用通配符。看看Collections的copy方法:

  public static <T> void copy(List<? super T> dest, List<? extends T> src) { 
        ...
        ListIterator<? super T> di=dest.listIterator();
        ListIterator<? extends T> si=src.listIterator();
        ...
 }

之所以这样,是因为这样确保源集合中的元素可以放入目标集合中,如果目标集合的泛型比源还窄的话,会出问题,这样使用super限制了目标集合一定比源集合类型要宽,编译时就起到了类型安全检测的作用了。

 


    * 类型安全 。 泛型的一个主要目标就是提高 Java 程序的类型安全。使用泛型可以使编译器知道变量的类型限制,进而可以在更高程度上验证类型假设。如果没有泛型,那么类型的安全性主要由程序员来把握,这显然不如带有泛型的程序安全性高。
    * 消除强制类型转换 。泛型可以消除源代码中的许多强制类型转换,这样可以使代码更加可读,并减少出错的机会。
    * 向后兼容。支持泛型的 Java 编译器(例如 JDK5.0 中的 Javac)可以用来编译经过泛型扩充的 Java 程序(GJ 程序),但是现有的没有使用泛型扩充的 Java 程序仍然可以用这些编译器来编译。
    * 层次清晰,恪守规范 。无论被编译的源程序是否使用泛型扩充,编译生成的字节码均可被虚拟机接受并执行。也就是说不管编译器的输入是 GJ 程序,还是一般的 Java 程序,经过编译后的字节码都严格遵循《Java 虚拟机规范》中对字节码的要求。可见,泛型主要是在编译器层面实现的,它对于 Java 虚拟机是透明的。
    * 性能收益 。目前来讲,用 GJ 编写的代码和一般的 Java 代码在效率上是非常接近的。 但是由于泛型会给 Java 编译器和虚拟机带来更多的类型信息,因此利用这些信息对 Java 程序做进一步优化将成为可能。
总之:为了避免JVM大的变动,java选择了在字节码级别保持向后兼容,而编译器则仅仅负责了检查以及自动添加强制转换代码来生成能兼容以前jvm的设计。

分享到:
评论
1 楼 how 2015-02-12  
                                     55555555555555555  

相关推荐

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

    Java泛型的用法及T.class的获取过程解析 Java泛型是Java编程语言中的一种重要特性,它允许开发者在编写代码时指定类型参数,从而提高代码的灵活性和可读性。本文将详细介绍Java泛型的用法 及T.class的获取过程解析...

    Java泛型三篇文章,让你彻底理解泛型(super ,extend等区别)

    Java 泛型详解 Java 泛型是 Java SE 5.0 中引入的一项特征,它允许程序员在编译时检查类型安全,从而减少了 runtime 错误的可能性。泛型的主要优点是可以Reusable Code,让程序员编写更加灵活和可维护的代码。 ...

    Java泛型应用实例

    Java泛型是Java编程语言中的一个强大特性,它允许我们在定义类、接口和方法时指定类型参数,从而实现代码的重用和类型安全。在Java泛型应用实例中,我们可以看到泛型如何帮助我们提高代码的灵活性和效率,减少运行时...

    很好的Java泛型的总结

    Java泛型机制详解 Java泛型是Java语言中的一种机制,用于在编译期检查类型安全。Java泛型的出现解决了Java早期版本中类型安全检查的缺陷。Java泛型的好处是可以在编译期检查类型安全,避免了运行时的...

    java 泛型类的类型识别示例

    综上所述,虽然Java泛型在编译后会进行类型擦除,但通过上述技巧,我们仍然能够在运行时获得关于泛型类实例化类型的一些信息。在实际开发中,这些方法可以帮助我们编写更加灵活和安全的代码。在示例文件`GenericRTTI...

    java泛型技术之发展

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着Java SE 5.0的发布而引入,极大地增强了代码的类型安全性和重用性。本篇文章将深入探讨Java泛型的发展历程、核心概念以及其在实际开发中的应用。 1. **发展...

    SUN公司Java泛型编程文档

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着JDK 5.0的发布被引入。这个特性极大地提高了代码的类型安全性和可读性,减少了在运行时出现ClassCastException的可能性。SUN公司的Java泛型编程文档,包括...

    java 泛型接口示例

    下面我们将详细探讨Java泛型接口的相关知识点。 1. **泛型接口的定义** 泛型接口的定义方式与普通接口类似,只是在接口名之后添加了尖括号`&lt;T&gt;`,其中`T`是一个类型参数,代表某种未知的数据类型。例如: ```java...

    java 泛型方法使用示例

    下面我们将深入探讨Java泛型方法的概念、语法以及使用示例。 **一、泛型方法概念** 泛型方法是一种具有类型参数的方法,这些类型参数可以在方法声明时指定,并在方法体内部使用。与类的泛型类似,它们提供了编译时...

    java泛型的内部原理及更深应用

    Java泛型是Java编程语言中的一个强大特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。这使得代码更加安全、可读性更强,并且能够减少类型转换的必要。在“java泛型的内部原理及更深应用”这个...

    JAVA泛型加减乘除

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

    Java 泛型擦除后的三种补救方法

    Java 泛型是一种强大的工具,它允许我们在编程时指定变量的类型,提供了编译时的类型安全。然而,Java 的泛型在运行时是被擦除的,这意味着在运行时刻,所有的泛型类型信息都会丢失,无法直接用来创建对象或进行类型...

    java泛型学习ppt

    "Java 泛型学习" Java 泛型是 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类。泛型的主要目标是提高 Java 程序的类型安全。通过知道使用泛型定义的变量的类型限制,编译器可以在一个高得多的...

    Java泛型使用详细分析.pdf

    Java 泛型使用详细分析 Java 泛型是 Java 语言中的一种类型系统特性,允许开发者在编译期检查类型安全,以避免在运行时出现类型相关的错误。在本文中,我们将详细介绍 Java 泛型的使用方法和实现原理。 一、泛型的...

    Java泛型技术之发展.pdf

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着Java SE 5.0的发布而引入,极大地增强了代码的类型安全性和重用性。本篇文章将深入探讨Java泛型的发展历程、核心概念以及其在实际开发中的应用。 1. **发展...

    面试必须资料java泛型攻略、

    #### 一、什么是Java泛型? Java泛型(Generics)是一种在编译时确保类型安全的机制,它允许程序员编写类型安全的通用类或方法,而无需进行显式的类型转换。在Java 1.5引入泛型之前,集合类(如`ArrayList`)只能...

Global site tag (gtag.js) - Google Analytics