`
froast
  • 浏览: 254220 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Generics(转载)

阅读更多
Generics
1) Introduction引言
1.1)为什么需要泛型?
Generics add stability to your code by making more of your bugs detectable at compile time. (泛型让你的bugs在编译期间就能被更多地发现。)
Compile-time bugs, for example, tell you immediately that something is wrong, you can use the compiler's error messages to figure out what the problem is and fix it; Runtime bugs, however, can be much more problematic; they don't always surface immediately, and when they do, it may be at a point in time that's far removed from the actual cause of the problem.
(举例来说,编译期的bugs,你能很快的得到错误信息,然后你可以根据出错提示知道问题所在并修复它;然后运行期的bugs就是一个难题,它们并不总是立刻显现出来,当它们出现在某一点的时候,可能离实际问题的根源已经很远了。)
1.2)关于本章泛型的学习
Some programmers choose to learn generics by studying the Java Collections Framework; after all, generics are heavily used by those classes. However, since we haven't yet covered collections, this chapter will focus primarily on simple "collections-like" examples that we'll design from scratch. This hands-on approach will teach you the necessary syntax and terminology while demonstrating the various kinds of problems that generics were designed to solve.
(一些程序员会能过学习collections来掌握generics。因为这些类过于复杂,我们在本章节我们主要的介绍一类似于”collections-like”的例子。作为一种过渡方法,它将教给你关于泛型概念必要的语法和术语。)
1.3)一个小例子让我们先看一个非Generics的例子中是如何出现run-time的bug
Box类能处理任何类型的对象
public class Box {
        private Object object;
        public void add(Object object) {
            this.object = object;
        }
        public Object get() {
            return object;
        }
    }
当我们要求它处理整型时,可能会有这样的用法:
public class BoxDemo1 {
    public static void main(String[] args) {
        // ONLY place Integer objects into this box!
        Box integerBox = new Box();
        integerBox.add(new Integer(10));
        Integer someInteger = (Integer)integerBox.get();
        System.out.println(someInteger);
    }
}

但有时一个粗心的程序员也可能要求它处理String,但却这样用了
public class BoxDemo2 {
    public static void main(String[] args) {
        // ONLY place Integer objects into this box!
        Box integerBox = new Box();
        // Imagine this is one part of a large application
        // modified by one programmer.
        integerBox.add("10"); // note how the type is now String
        // ... and this is another, perhaps written
        // by a different programmer
        Integer someInteger = (Integer)integerBox.get();
        System.out.println(someInteger);
    }
}
编译此程序,在编译期间,编译器总是假设强制类型转换是正确的,只有在运行时候,你才能得到下列信息:
Exception in thread "main"
         java.lang.ClassCastException:
            java.lang.String cannot be cast to java.lang.Integer
            at BoxDemo2.main(BoxDemo2.java:6)
If the Box class had been designed with generics in mind, this mistake would have been caught by the compiler, instead of crashing the application at runtime
(如果Box类用泛型来定义,那么这样的错误将在编译期被抓住,而不是在程序运行时候crash。下一节我们将看看什么泛型类型的定义。)

2) Generic Types
2.1)定义泛型类
We introduces type variable, named T, that can be used anywhere inside the class. Just think of T as a special kind of variable, whose "value" will be whatever type you pass in; Also, It just can't be any of the primitive data types.
(我们引入一个名叫T的类型变量,它在类中的任何地方使用。你可以把T认为是一种特殊类型的变量,它的值可以任何你想传入的类型,当然,T不能接受任何原始数据类型。)
/**
* Generic version of the Box class.
**/
public class Box<T> {
    private T t; // T stands for "Type"        
    public void add(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
}
2.2)实例化一个泛型类
To instantiate this class, use the new keyword, as usual, but place <Integer> between the class name and the parenthesis;
Box<Integer> integerBox = new Box<Integer>();
(实例化一个泛类,要用到一个新的关键字,仅仅是加了<Integer>在类名和()之间。)
2.3)使用这个泛型
public class BoxDemo3 {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.add(new Integer(10));
        Integer someInteger = integerBox.get(); // no cast!
        System.out.println(someInteger);
    }
}
注意在泛型类中我们并没有象非泛型那样使用强制类型转换(Cast Type)。另一方面就象我们分析泛型所应有的优点那样,如果你试图加一个不兼容的类型到这个box类中,比如说String那么就会在编译期得到错误报告。
public class BoxDemo3 {
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.add(“10”);
       String someInteger = integerBox.get(); // no cast!
        System.out.println(someInteger);
    }
}
你将会得到一个错误的编译信息
BoxDemo3.java:5: add(java.lang.Integer) in Box<java.lang.Integer>  
    cannot be applied to (java.lang.String)
        integerBox.add("10");
               ^
    1 error
2.4)理解泛型
In the above examples, you won't find T.java or T.class anywhere on the filesystem. Furthermore, T is not a part of the Box class name. In fact during compilation, all generic information will be removed entirely, leaving only Box.class on the filesystem.
(在上面的例子中,你不能在文件中发现T.java或T.class。而且T根本就不是Box类名的一部分。事实上,在编译期,所有的泛型信息将会被整个移去,只有与泛型类型相对应的Box.class文件存在。)
2.5)泛型参数的命名约定 Type Parameter Naming Conventions
E - Element (used extensively by the Java Collections Framework)
K – Key
N – Number
T – Type
V – Value
S,U,V etc. - 2nd, 3rd, 4th types

Also note that a generic type may have multiple type parameters, but each parameter must be unique within its declaring class or interface. A declaration of Box<T,T>, for example, would generate an error on the second occurrence of T, but Box<T,U>, however, would be allowed.
(泛型允许有多个类型参数,但每个类型参数必须是在同一个声明类中是独一无二的。如Box<T,T>的声明就会引起一个编译错误,而Box<T,U>才是被允许形式。)
3)泛型方法与构造函数Generic Methods and Constructors
Type parameters can also be declared within method and constructor signatures to create generic methods and generic constructors. This is similar to declaring a generic type, but the type parameter's scope is limited to the method or constructor in which it's declared.
泛型参数也可以允许在方法和构造中定义,-这时方法和构造就被称之谓泛型方法和泛型参数。类似于泛型类的定义,但是在方法和构造中所定义的类型参数的作用域只能在定义它们的方法和构造中。
/**
* This version introduces a generic method.
*/
public class Box<T> {
    private T t;        
    public void add(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
    public <U> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.add(new Integer(10));
        integerBox.inspect("some text");
    }
By passing in different types, the output will change accordingly.
( inspect( )传入不同的类型,将会得到相应的结果。)
4)限制类型参数Bounded Type Parameters
4.1)There may be times when you'll want to restrict the kinds of types that are allowed to be passed to a type parameter. For example, a method that operates on numbers might only want to accept instances of Number or its subclasses. (很多情况下,你需要限制你允许传给类型参数的种类。比如,一个操作各种Number类的方法可能仅仅需要接收Number类或者它的子类对象。)
To declare a bounded type parameter, list the type parameter's name, followed by the extends keyword, followed by its upper bound, which in this example is Number. Note that, in this context, extends is used in a general sense to mean either "extends" (as in classes) or "implements" (as in interfaces).
(要定义一个限定的类型参数,需要列出这个类型参数名,后面跟上extends关键字,最后加上限边界类名upper bound,在本例中,这个upper bound 是Number。注意:在这里,extends被用作一个广泛的含义既可以是extends,当upper bound是一个类时,也可以是implements当upper bound 是一个接口。 如下面的例子)
/**
* This version introduces a bounded type parameter.
*/
public class Box<T> {
    private T t;        
    public void add(T t) {
        this.t = t;
    }
    public T get() {
        return t;
    }
    public <U extends Number> void inspect(U u){
        System.out.println("T: " + t.getClass().getName());
        System.out.println("U: " + u.getClass().getName());
    }
    public static void main(String[] args) {
        Box<Integer> integerBox = new Box<Integer>();
        integerBox.add(new Integer(10));
        integerBox.inspect("some text"); // error: this is still String!
    }
这时候,如果我们编译该程序的话,会在编译期得到下面的出错信息。
Box.java:21: <U>inspect(U) in Box<java.lang.Integer> cannot
be applied to (java.lang.String)
                        integerBox.inspect("10");
                                  ^
1 error
4.2)多重界定
To specify additional interfaces that must be implemented, use the & character, as in:
<U extends Number & MyInterface>


5)泛类-子类型Subtyping
5.1) Is a relation of Class
As you already know, it's possible to assign an object of one type to an object of another type provided that the types are compatible. For example, you can assign an Integer to an Object, since Object is one of Integer's supertypes:
    Object someObject = new Object();
    Integer someInteger = new Integer(10);
    someObject = someInteger; // OK
(就象你已经知道的,可以给一个对象分配一个与它类型兼容的对象。如:你能把Integer分配给一个Object,因为Object是Integer的父类。)
5.2) Is a relation of Generic
The same is also true with generics. You can perform a generic type invocation, passing Number as its type argument and any subsequent invocation of add will be allowed if the argument is compatible with Number:
    Box<Number> box = new Box<Number>();
    box.add(new Integer(10)); // OK
    box.add(new Double(10.1)); // OK
(这个规则也适用于generics。你能定义一个generic类,以Number作为类型参数,那么你就可以用add方法传递任何与Number相兼容的类型。)
5.3)泛型类作为一个参数
Now consider the following method:
(现在考虑下面的方法:)
    public void boxTest(Box<Number> n){
        // method body omitted
    }
What type of argument does it accept? By looking at its signature, we can see that it accepts a single argument whose type is Box<Number>. Are you allowed to pass in Box<Integer> or Box<Double>, as you might expect? Surprisingly, the answer is "no", because Box<Integer> and Box<Double> are not subtypes of Box<Number>.
(它能接受哪一种参数?通过它的定义,我们能知道它接受简单的参数Box<Number>.猜想一下是否可以传入Box<Integer> or Box<Double>?答案是“不行”,因为Box<Integer> or Box<Double>不是Box<Number>的子类。
如下面的代码片段:
Box<Number> box=new Box<Number>;
Box<Integer> intBox=new Box<Integer>;
Box<Double> douBox=new Box<Double>;
box=intBox; //compiler error
box=douBox; //compiler error
)

5)泛类-通配符Wildcards
结合日常实际,你可以把泛型想象成一个更为实际的东西----笼子。
针对第4点的编译错误,我们能用下面的定义解决:
Box<? Extends Number> someBox=…;
In generics, an unknown type is represented by the wildcard character "?".
To specify a cage capable of holding some kind of animal:
       Cage<? extends Animal> someCage = ...;
Read "? extends Animal" as "an unknown type that is a subtype of Animal, possibly Animal itself".
(在泛型中,通配符?能表示一个未知类型。比如为你可以定义一个泛类(动物类的笼子)来存放不同动物的笼子。”? extends Animal” 表示解读是动物类的一个未知子类,也可能是动物类自己。)
Note: It's also possible to specify a lower bound by using the super keyword instead of extends. The code <? super Animal>, therefore, would be read as "an unknown type that is a supertype of Animal, possibly Animal itself". You can also specify an unknown type with an unbounded wildcard, which simply looks like <?>. An unbounded wildcard is essentially the same as saying <? extends Object>.
(注意:也可以用super来指定一个下界类, 因此<?super Animal>将会被解读为’是动物类的一个未知的父类,也可能是Animal类自己’。还可以指定一个没有上下界定的通配符,就象这样<?>表示的意思是<? extends Object>)。
Box<? extends Number> box=new Box<? extends Number>;
Box<Integer> intBox=new Box<Integer>;
Box<Double> douBox=new Box<Double>;
box=intBox; //compiler OK
box=douBox; //compiler OK
(对此我们可以这样理解,尽管Box<Integer>和Box<Double>不是Box<Number>的子类,但是它们在事实上可以认为是Box<? extends Number>的子类。那接下来的问题是我们能否把
box.add(new Integer(10)); //compiler error
box.add(new Double(10.34));//compiler error
我们可以用笼子理论来解释这种行为,box定义为可以关各种动物类及其子类的笼子,所以作为笼子类它可以表示其它动物类的笼子。所以box=intBox;正确地。但是如果你想把各种动物关进这一种叫动物类的笼子,就不合适了。所以box.add(new Integer(10))是不正确的。
那么我们如何使用box类呢?它是无用的吗?当然不是。你可以定义这样的一方法
public void testBox(Box<? extends Number> n){
    System.out.print("invoke-cage-class,you can try pass GenericBox<Integer>");
}
应用如下:
Box<? extends Number> box=new Box<? extends Number>;
Box<Integer> intBox=new Box<Integer>;
Box<Double> douBox=new Box<Double>;
box=intBox; //compiler OK
box=douBox; //compiler OK
testBox(intBox);//compiler OK
testBox(douBox);//compiler OK
(注这节,本人现在并不太了解,它什么用意,能这样传输泛类目的何在?2007-6-14)



6)泛类的擦除Type Erasure
6.1) 什么叫类擦除技术?When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.
(当一个泛型类被实例化时,编译器会通过一种叫类擦除的技术转换类参数---会在类或方法中移除所有关于类型参数的信息。类擦除技术能使Java采用泛型的应用程序去兼容未使用泛型类的Java库和Java应用程序。)
For instance, Box<String> is translated to type Box, which is called the raw type — a raw type is a generic class or interface name without any type arguments. This means that you can't find out what type of Object a generic class is using at runtime. The following operations are not possible:
(举例来说,Box<String>在编译时会被转换成Box类,被叫做原始类---原始类就是一个没有任何类参数的泛型类或泛型接口。那就意味着你不可能在运行时刻发现任何泛型类对象。下面的操作是不可能通过的:)
public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) { //Compiler error
            ...
        }
        E item2 = new E();   //Compiler error
        E[ ] iArray = new E[10]; //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
}
The operations shown in bold are meaningless at runtime because the compiler removes all information about the actual type argument (represented by the type parameter E) at compile time.
(在上面黑体部分的操作在运行期是无意义的,因为编译器在编译期就把实际的类参数(即E)全部移走了。)
6.2)类擦除存在的意义
Type erasure exists so that new code may continue to interface with legacy code. Using a raw type for any other reason is considered bad programming practice and should be avoided whenever possible.
When mixing legacy code with generic code, you may encounter warning messages similar to the following:
Note: WarningDemo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
(正是因为类擦除的存在所以新代码才能继续使用老的遗留代码,比如在下面的例子中Integer类型符号在编译时会被除去,从而可以继续使用老库中非泛型的方法。但要注意它会返一个warning)
public class WarningDemo {
    public static void main(String[] args){
        Box<Integer> bi;
        bi = createBox();  
    }
    /**
     * Pretend that this method is part of an old library,
     * written before generics. It returns Box instead of Box<T>.
     */
    static Box createBox(){
        return new Box();
    }
}
Recompiling with -Xlint:unchecked reveals the following additional information:
WarningDemo.java:4: warning: [unchecked] unchecked conversion
found   : Box
required: Box<java.lang.Integer>
        bi = createBox();
                      ^
1 warning
7)Exercise . Design a class that acts as a library for the following kinds of media: book, video, and newspaper. Provide one version of the class that uses generics and one that does not. Feel free to use any additional APIs for storing and retrieving the media.

Answer 1:
Non-Generic Version
import java.util.List;
import java.util.ArrayList;
public class Library {
    private List resources = new ArrayList();
    public void addMedia(Media x) {
        resources.add(x);
    }
    public Media retrieveLast() {
        int size = resources.size();
        if (size > 0) {
            return (Media)resources.get(size - 1);
        }
        return null;
    }
}

interface Media {
}
interface Book extends Media {
}
interface Video extends Media {
}
interface Newspaper extends Media {
}

Generic Version
import java.util.List;
import java.util.ArrayList;

public class Library<E extends Media> {
    private List<E> resources = new ArrayList<E>();
    public void addMedia(E x) {
        resources.add(x);
    }
    public E retrieveLast() {
        int size = resources.size();
        if (size > 0) {
            return resources.get(size - 1);
        }
        return null;
    }
}
分享到:
评论

相关推荐

    Java Generics and Collections

    在Java中,泛型(Generics)引入于J2SE 5.0,目的是提供类型安全的集合,避免了在集合操作中强制类型转换的需要,同时也消除了运行时可能发生的ClassCastException。而集合框架(Collections Framework)是一组接口...

    generics-tutorial.pdf

    自JDK 1.5以来,Java编程语言引入了一系列重要的扩展功能,其中最重要的一个特性就是泛型(Generics)。本文档旨在向读者介绍Java中的泛型,并通过与类似语言如C++中的模板进行对比,帮助读者理解其相似性和差异性。...

    泛型generics.zip

    泛型(Generics)是JDK5引入的一种参数化类型特性。它提供了编译时类型安全检测机制。其本质是参数类型,所操控的数据类型被指定为一个参数。泛型不存在于JVM虚拟机。泛型学习、泛型限定类型、泛型通配符、泛型继承。...

    Java Generics FAQ

    Java泛型(Generics)是Java SE 5.0引入的一种新特性,它允许程序员在编译时检查类型安全,并且所有的强制转换都是自动和隐式的,提高了代码的重用率。下面将根据给定的文件信息,深入解析Java泛型的一些核心知识点...

    Generics的用法

    在Java编程语言中,泛型(Generics)是一种强大的特性,它允许我们在类、接口和方法中使用类型参数,从而增强了代码的类型安全性和重用性。本篇将深入探讨泛型的用法,以及其在源码和工具中的应用。 1. **泛型的...

    .Net 2.0 泛型高级编程(Professional .NET 2.0 Generics )

    《.Net 2.0 泛型高级编程》是一本由Tod Golding编著的专业技术书籍,专注于探讨.NET Framework 2.0中的泛型特性。泛型是.NET框架中的一个重要概念,它允许开发者创建可重用的类型安全的数据结构和算法,极大地提高了...

    Java Generics and Collections (Java泛型与集合)

    本书"Java Generics and Collections"深入探讨了这两个主题,帮助开发者编写更安全、更高效且可维护的代码。 首先,让我们来理解Java泛型。泛型是Java 5引入的一项特性,它允许在类、接口和方法中使用类型参数。这...

    generics:通用方法

    仿制药 generics旨在通过提供一组可以导入的泛型方法来帮助程序包作者减少依赖性。 例如,如果包作者希望为其对象包括一个tidy方法,则他们必须导入broom包才能这样做。 这会起作用,但可能会增加安装和/或测试...

    delphi-generics.rar_delphi Generics_delphi2009_made

    CodeGear Delphi2009 has lots of new features like use of Generics in Delphi. Generic methods, procedures and data types are made easy to use datatype free working with you classes.

    Generics_in_the_Java_Programming_Language译文

    Java 泛型详解 Java 中的泛型是 Java 5(JDK 1.5)中引入的一项新特性,旨在解决类型安全和代码重用的问题。泛型允许程序员对类型进行抽象,使得代码更加灵活和可维护。 泛型的优点: 1. 类型安全:泛型可以在...

    Collections & Generics in J2SE 1.5

    ### Collections与Generics在J2SE 1.5中的应用 #### 快速回顾Collections 在深入了解Generics之前,我们先快速回顾一下Java中的Collections框架。Collections是Java平台的一个核心特性,它为开发者提供了一组丰富...

    generics C#

    "Generics C#"这个主题深入探讨了如何在C#编程中有效地使用泛型。 在C#中,泛型的主要优点包括: 1. **类型安全**:泛型提供了编译时的类型检查,确保了在运行时不会出现类型转换异常。这通过在实例化泛型类型时...

    [Java泛型和集合].(Java.Generics.and.Collections).文字版

    本资料 "[Java泛型和集合].(Java.Generics.and.Collections).Maurice.Naftalin&amp;Philip.Wadler.文字版" 由知名专家Maurice Naftalin和Philip Wadler编著,提供了关于这些主题的深入理解。 **Java泛型** 是自Java...

Global site tag (gtag.js) - Google Analytics