- 浏览: 302487 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (271)
- jBPM (0)
- WebService (10)
- Flex (0)
- RubyOnRails (1)
- Java (56)
- J2EE (2)
- SQL (5)
- Tapestry (2)
- Dom4j (1)
- Japanese (9)
- English (4)
- JavaScript (3)
- Ajax (12)
- MyDiary (3)
- Log4j (2)
- XML (3)
- UML (1)
- Struts (3)
- Others (8)
- Funny (7)
- ProjectManagement (3)
- Tomcat (1)
- Servlet&Jsp (6)
- Html (4)
- iBATIS (1)
- EasyMock (1)
- Astronomy (1)
- Biology (1)
- Food and Health (0)
最新评论
-
yet8telecom:
good
js数组 sort方法的分析 转自NorthSnow HOME -
imain:
最后的结果是:1,5,3,4,6,2
js数组 sort方法的分析 转自NorthSnow HOME -
lixiaoqing:
最后一个输出结果应该是3,5,1,4,2,6 吧?
js数组 sort方法的分析 转自NorthSnow HOME -
benxiaohai1212:
请问如果输入参数是数组array类型,怎么处理?谢谢!
实例讲解:JAVA SOAP技术 -
netdisk:
这个建议可以用在电子文档管理上
软件文档管理指南
版权声明
:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
英文原文地址:
http://www.onjava
.com/pub/a/onjava
/2005/07/06/generics.html
中文地址:
http://www.matrix.org.cn/resource/article/43/43634_java
_generics.html
关键词: java
generics java
5
摘要
泛型是j2se
5.0最重要的特性。他们让你写一个type(类或接口)和创建一个实例通过传递一个或多个引用类型。这个实例受限于只能作用于这些类型。比如,在java
5,java
.util.list 已经被泛化。当建立一个list对象时,你通过传递一个java
类型建立一个list实例,此list实例只能作用于所传递的类型。这意味着如果你传递一个string ,此list实例只能拥有string对象;如果你传递一个integer,此实例只能存贮integer对象。除了创建参数化的类型,你还能创建参数化的函数。
泛型的第一个好处是编译时的严格类型检查。这是集合框架
最重要的特点。此外,泛型消除了绝大多数的类型转换。在jdk 5.0之前,当你使用集合框架
时,你不得不进行类型转换。
本文将教你如何操作泛型。它的第一部分是“没有泛型的日子”,先让我们回忆老版本jdk的不便。然后,举一些泛型的例子。在讨论完语法以及有界泛型的使用之后,文章最后一章将解释如何写泛型。
没有泛型的日子
所有的java
类都源自java
.lang.object,这意味着所有的java
对象能转换成object。因此,在之前的jdk的版本中,很多集合框架
的函数接受一个object参数。所以,collections是一个能持有任何对象的多用途工具,但带来了不良的后果。
举个简单的例子,在jdk 5.0的之前版本中,类list的函数add接受一个object参数:
public boolean add(java .lang.object element)
所以你能传递任何类型给add。这是故意这么设计的。否则,它只能传递某种特定的对象,这样就会出现各种list类型,如,stringlist, employeelist, addresslist等。
add通过object传递能带来好处,现在我们考虑get函数(返回list中的一个元素).如下是jdk 5之前版本的定义:
public java .lang.object get(int index) throws indexoutofboundsexception
get返回一个object.不幸的事情从此开始了.假如你储存了两个string对象在一个list中:
list stringlist1 = new arraylist(); stringlist1.add("java 5"); stringlist1.add("with generics");
当你想从stringlist1取得一个元素时,你得到了一个object.为了操作原来的类型元素,你不得不把它转换为string。
string s1 = (string) stringlist1.get(0);
但是,假如你曾经把一个非string对象加入stringlist1中,上面的代码会抛出一个classcastexception. 有了泛型,你能创建一个单一用途的list实例.比如,你能创建一个只接受string对象的list实例,另外一个实例只能接受employee对象.这同样适用于集合框架
中的其他类型.
泛型入门
像一个函数能接受参数一样,一个泛型也能接受参数.这就是一个泛型经常被称为一个参数化类型的原因.但是不像函数用()传递参数,泛型是用<>传递参数的.声明一个泛型和声明一个普通类没有什么区别,只不过你把泛型的变量放在<>中.
比如,在jdk 5中,你可以这样声明一个java
.util.list : list<e> mylist;
e 称为类型变量.意味着一个变量将被一个类型替代.替代类型变量的值将被当作参数或返回类型.对于list接口来说,当一个实例被创建以后,e 将被当作一个add或别的函数的参数.e 也会使get或别的参数的返回值.下面是add和get的定义:
boolean add<e o> e get(int index)
note:
一个泛型在声明或例示时允许你传递特定的类型变量: e.除此之外,如果e是个类,你可以传递子类;如果e是个接口,你可以传递实现接口的类;
-----------------------------译者添加--------------------
list<number> numberlist= new arraylist<number>();
numberlist.add(2.0);
numberlist.add(2);
-----------------------------译者添加--------------------
如果你传递一个string给一个list,比如:
list<string> mylist;
那么mylist的add函数将接受一个string作为他的参数,而get函数将返回一个string.因为返回了一个特定的类型,所以不用类型转化了。
note
:根据惯例,我们使用一个唯一的大写字目表示一个类型变量。为了创建一个泛型,你需在声明时传递同样的参数列表。比如,你要想创建一个arraylist来操作string ,你必须把string放在<>中。如:
list<string> mylist = new arraylist<string>();
再比如,java
.util.map 是这么定义的:
public interface map<k,v>
k用来声明map键(key)的类型而v用来表示值(value)的类型。put和values是这么定义的:
v put(k key, v value) collection<v> values()
note
:一个泛型不准直接的或间接的是java
.lang.throwable的子类。因为异常是在运行时抛出的,所以它不可能预言什么类型的异常将在编译时抛出.
列表1的例子将比较list在jdk 1.4 和jdk1.5的不同
package com.brainysoftware.jdk5.app16; import java .util.list; import java .util.arraylist; public class genericlisttest { public static void main(string[] args) { // in jdk 1.4 list stringlist1 = new arraylist(); stringlist1.add("java 1.0 - 5.0"); stringlist1.add("without generics"); // cast to java .lang.string string s1 = (string) stringlist1.get(0); system.out.println(s1.touppercase()); // now with generics in jdk 5 list<string> stringlist2 = new arraylist<string>(); stringlist2.add("java 5.0"); stringlist2.add("with generics"); // no need for type casting string s2 = stringlist2.get(0); system.out.println(s2.touppercase()); } }
在列表1中,stringlist2是个泛型。声明list<string>告诉编译器list的实例能接受一个string对象。当然,在另外的情况中,你能新建能接受各种对象的list实例。注意,当从list实例中返回成员元素时,不需要对象转化,因为他返回的了你想要的类型,也就是string.
note
:泛型的类型检查(type checking)是在编译时完成的.
最让人感兴趣的事情是,一个泛型是个类型并且能被当作一个类型变量。比如,你想你的list储存lists of strings,你能通过把list<string>作为他的类型变量来声明list。比如:
list<list<string>> mylistoflistsofstrings;
要从mylist中的第一个list重新取得string,你可以这么用:
string s = mylistoflistsofstrings.get(0).get(0);
下一个列表中的listofliststest类示范了一个list(命名为listoflists)接受一个string list作为参数。
package com.brainysoftware.jdk5.app16; import java .util.arraylist; import java .util.list; public class listofliststest { public static void main(string[] args) { list<string> listofstrings = new arraylist<string>(); listofstrings.add("hello again"); list<list<string>> listoflists = new arraylist<list<string>>(); listoflists.add(listofstrings); string s = listoflists.get(0).get(0); system.out.println(s); // prints "hello again" } }
另外,一个泛型接受一个或多个类型变量。比如,java
.util.map有两个类型变量s。第一个定义了键(key)的类型,第二个定义了值(value)的类型。下面的例子讲教我们如何使用个一个泛型map.
package com.brainysoftware.jdk5.app16; import java .util.hashmap; import java .util.map; public class maptest { public static void main(string[] args) { map<string, string> map = new hashmap<string, string>(); map.put("key1", "value1"); map.put("key2", "value2"); string value1 = map.get("key1"); } }
在这个例子中,重新得到一个key1代表的string值,我们不需要任何类型转换。
没有参数的情况下使用泛型
既然在j2se
5.0中收集类型已经泛型化,那么,原来的使用这些类型的代码将如何呢?很幸运,他们在java
5中将继续工作,因为你能使用没有参数的泛型。比如,你能继续像原来一样使用list接口,正如下面的例子一样。
list stringlist1 = new arraylist(); stringlist1.add("java 1.0 - 5.0"); stringlist1.add("without generics"); string s1 = (string) stringlist1.get(0);
一个没有任何参数的泛型被称为原型(raw type)。它意味着这些为jdk1.4或更早的版本而写的代码将继续在java
5中工作。
尽管如此,一个需要注意的事情是,jdk5编译器希望你使用带参数的泛型。否则,编译器将提示警告,因为他认为你可能忘了定义类型变量s。比如,编译上面的代码的时候你会看到下面这些警告,因为第一个list被认为是原型。
note: com/brainysoftware/jdk5/app16/genericlisttest.java
uses unchecked or unsafe operations.
note: recompile with -xlint:unchecked for details.
当你使用原型时,如果你不想看到这些警告,你有几个选择来达到目的:
1.编译时带上参数-source 1.4
2.使用@supresswarnings("unchecked")注释
3.更新你的代码,使用list<object>. list<object>的实例能接受任何类型的对象,就像是一个原型list。然而,编译器不会报错。
使用 ? 通配符
前面提过,如果你声明了一个list<atype>, 那么这个list对atype起作用,所以你能储存下面这些类型的对象:
1.一个atype的实例
2.它的子类的实例(如果atype是个类)
3.实现atype接口的类实例(如果atype是个接口)
但是,请注意,一个泛型本身是个java
类型,就像java
.lang.string或java
.io.file一样。传递不同的类型变量给泛型可以创建不同的java
类型。比如,下面例子中list1和list2引用了不同的类型对象。
list<object> list1 = new arraylist<object>(); list<string> list2 = new arraylist<string>();
list1指向了一个类型变量s为java
.lang.objects 的list而list2指向了一个类型变量s为string 的list。所以传递一个list<string>给一个参数为list<object>的函数将导致compile time错误。下面列表可以说明:
package com.brainysoftware.jdk5.app16; import java .util.arraylist; import java .util.list; public class allowedtypetest { public static void doit(list<object> l) { } public static void main(string[] args) { list<string> mylist = new arraylist<string>(); // 这里将产生一个错误 doit(mylist); } }
上面的代码无法编译,因为你试图传递一个错误的类型给函数doit。doit的参数是list<object>二你传递的参数是list<string>。
可以使用 ? 通配符解决这个难题。list<?> 意味着一个对任何对象起作用的list。所以,doit可以改为:
public static void doit(list<?> l) {}
在某些情况下你会考虑使用 ? 通配符。比如,你有一个printlist函数,这个函数打印一个list的所有成员,你想让这个函数对任何类型的list起作用时。否则,你只能累死累活的写很多printlist的重载函数。下面的列表引用了使用 ? 通配符的printlist函数。
package com.brainysoftware.jdk5.app16; import java .util.arraylist; import java .util.list; public class wildcardtest { public static void printlist(list<?> list) { for (object element : list) { system.out.println(element); } } public static void main(string[] args) { list<string> list1 = new arraylist<string>(); list1.add("hello"); list1.add("world"); printlist(list1); list<integer> list2 = new arraylist<integer>(); list2.add(100); list2.add(200); printlist(list2); } }
这些代码说明了在printlist函数中,list<?>表示各种类型的list对象。然而,请注意,在声明的时候使用 ? 通配符是不合法的,像这样:
list<?> mylist = new arraylist<?>(); // 不合法
如果你想创建一个接收任何类型对象的list,你可以使用object作为类型变量,就像这样:
list<object> mylist = new arraylist<object>();
在函数中使用界限通配符
在之前的章节中,你学会了通过传递不同的类型变量s来创建不同java
类型的泛型,但并不考虑类型变量s之间的继承关系。在很多情况下,你想一个函数有不同的list参数。比如,你有一个函数getaverage,他返回了一个list中成员的平均值。然而,如果你把list<number>作为getaverage的参数,你就没法传递list<integer> 或list<double>参数,因为list<number>和list<integer> 和list<double>不是同样的类型。
你能使用原型或使用通配符,但这样无法在编译时进行安全
类型检查,因为你能传递任何类型的list,比如list<string>的实例。你可以使用list<number>作为参数,但是你就只能传递list<number>给函数。但这样就使你的函数功能减少,因为你可能更多的时候要操作list<integer>或list<long>,而不是list<number>。
j2se
5.0增加了一个规则来解决了这种约束,这个规则就是允许你定义一个上界(upper bound) 类型变量.在这种方式中,你能传递一个类型或它的子类。在上面getaverage函数的例子中,你能传递一个list<number>或它的子类的实例,比如list<integer> or list<float>。
使用上界规则的语法这么定义的:generictype<? extends upperboundtype>. 比如,对getaverage函数的参数,你可以这么写list<? extends number>. 下面例子说明了如何使用这种规则。
package com.brainysoftware.jdk5.app16; import java .util.arraylist; import java .util.list; public class boundedwildcardtest { public static double getaverage(list<? extends number> numberlist) { double total = 0.0; for (number number : numberlist) total += number.doublevalue(); return total/numberlist.size(); } public static void main(string[] args) { list<integer> integerlist = new arraylist<integer>(); integerlist.add(3); integerlist.add(30); integerlist.add(300); system.out.println(getaverage(integerlist)); // 111.0 list<double> doublelist = new arraylist<double>(); doublelist.add(3.0); doublelist.add(33.0); system.out.println(getaverage(doublelist)); // 18.0 } }
由于有了上界规则,上面例子中的getaverage函数允许你传递一个list<number> 或一个类型变量是任何java
.lang.number子类的list。
下界规则
关键字extends定义了一个类型变量的上界。通过使用super关键字,我们可以定义一个类型变量的下界,尽管使用的情况不多。比如,如果一个函数的参数是list<? super integer>,那么意味着你可以传递一个list<integer>的实例或者任何java
.lang.integer的超类(superclass)。
创建泛型
前面的章节主要说明了如何使使用泛型,特别是集合框架
中的类。现在我们开始学习如何写自己的泛型。
基本上,除了声明一些你想要使用的类型变量s外,一个泛型和别的类没有什么区别。这些类型变量s位于类型后面的<>中。比如,下面的point就是个泛型。一个point对象代表了一个系统中的点,它有横坐标和纵坐标。通过使point泛型化,你能定义一个点实例的精确程度。比如,如果一个point对象需要非常精确,你就把double作为类型变量。否则,integer 就够了。
package com.brainysoftware.jdk5.app16; public class point<t> { t x; t y; public point(t x, t y) { this.x = x; this.y = y; } public t getx() { return x; } public t gety() { return y; } public void setx(t x) { this.x = x; } public void sety(t y) { this.y = y; } }
在这个例子中,t是point的类型变量 。t是getx和gety的返回值类型,也是setx和sety的参数类型。此外,构造函数结合两个t参数。
使用point类就像使用别的类一样。比如,下面的例子创建了两个point对象:ponint1和point2。前者把integer作为类型变量,而后者把double作为类型变量。
point<integer> point1 = new point<integer>(4, 2); point1.setx(7); point<double> point2 = new point<double>(1.3, 2.6); point2.setx(109.91);
总结
泛型使代码在编译时有了更严格的类型检查。特别是在集合框架
中,泛型有两个作用。第一,他们增加了对集合类型在编译时的类型检查,所以集合类所能持有的类型对传递给它的参数类型起了限制作用。比如你创建了一个持有strings的java
.util.list实例,那么他就将不能接受integers或别的类型。其次,当你从一个集合中取得一个元素时,泛型消除了类型转换的必要。
泛型能够在没有类型变量的情况下使用,比如,作为原型。这些措施让java
5之前的代码能够运行在jre 5中。但是,对新的应用程序,你最好不要使用原型,因为以后java
可能不支持他们。
你已经知道通过传递不同类型的类型变量给泛型可以产生不同的java
类型。就是说list<string>和list<object>的类型是不同的。尽管string是java
.lang.object。但是传递一个list<string>给一个参数是list<object>的函数会参数会产生编译错误(compile error)。函数能用 ? 通配符使其接受任何类型的参数。list<?> 意味着任何类型的对象。
最后,你已经看到了写一个泛型和别的一般java
类没有什么区别。你只需要在类型名称后面的<>中声明一系列的类型变量s就行了。这些类型变量s就是返回值类型或者参数类型。根据惯例,一个类型变量用一个大写字母表示。
budi kurniawan是一个高级 j2ee
架构师和作家。作者:budi kurniawan
翻译:rr00
email:di_feng_ro@hotmail.com
发表评论
-
创建文件夹,用系统日期命名。
2006-06-08 17:46 950import java.util.*;import java. ... -
将不同目录下的文件,复制到同一个目录下。
2006-06-09 08:50 939import java.io.*;public class F ... -
字符串转换和toString()、字符截取charAt()、一次截取多个字符getChars()
2006-06-09 09:19 1335class Person{ String name; Stri ... -
对字符串数组进行排序,在冒泡法排序中使用compareTo()方法确定排序的顺序。
2006-06-09 11:13 1310//A bubble sort for Strings.pub ... -
使用substring()方法完成字符串替换.
2006-06-09 13:38 943/* 利用substring()方法可以截取字符串,它有两种形 ... -
JAVA编程规范
2006-06-09 16:51 987作者:李小敏 本文选自:IBM DW中国 2002年08月21 ... -
通过调用System.getProperty()方法来获得不同环境变量的值。
2006-06-10 20:11 1005class ShowUserDir{ public stati ... -
使用TreeSet()进行排序
2006-06-13 18:41 934/* TreeSet为使用树来进行存储的Set接口提供了一个工 ... -
使用LinkedList存储信箱地址
2006-06-13 18:51 858/* 除了在类集中存储用户定义的类之外,关于下面程序的另一个重 ... -
HashMap用法 示例
2006-06-14 08:48 1241/* 程序开始创建了一个散列映射,然后将名字的映射增加到平衡表 ... -
TreeMap用法 示例
2006-06-14 09:14 2169/* TreeMap类通过使用树来实现Map接口.TreeMa ... -
TreeSet() 类逆向排序(实现compare()方法以便按正常顺序的逆向进行操作)。
2006-06-14 10:28 2108/* 仔细观察实现Comparator并覆盖compare() ... -
TreeMap 排序
2006-06-14 15:34 2110/* 比较函数类TComp比较两个包含姓和名的字符串。它首先比 ... -
Arrays类中的一些方法 示例
2006-06-15 08:48 879//Demonstrate Arrays.import jav ... -
Vector Enumeration 示例
2006-06-15 09:47 1030//Demonstrate various vector op ... -
堆栈(Stack)示例
2006-06-15 10:44 840//Demonstrate the Stack class.i ... -
Hashtable 示例
2006-06-15 13:19 926//Demonstrate a Hashtable.impor ... -
Properties 示例
2006-06-15 14:48 882/* 为了更大的灵活性,当构造一个属性(Properties) ... -
Properties的store()方法和load()方法 示例
2006-06-15 15:31 1463/* Properties的一个最有用的方面是可以利用stor ... -
StringTokenizer(字符串标记) 示例
2006-06-15 16:56 857//Demonstrate StringTokenizer.i ...
相关推荐
泛型是 J2SE 5.0 中最重要的特性之一,允许在类、接口和方法中使用类型参数。这样可以确保在编译时期就进行类型检查,减少了运行时的类型转换,并提高了代码的可读性和安全性。 2. **枚举(Enums)** 之前,Java ...
Java API官方文档中文版CHM版(J2SE5.0)是Java开发的重要参考资料,它详尽地阐述了Java 2 Standard Edition 5.0(通常称为Java 5.0)中的各种类库和接口。这个CHM(Compiled HTML Help)文件是一个经过编译的HTML...
描述:“对J2SE 5.0中的一些新特性进行了精辟的讲解。” J2SE 5.0是Java Standard Edition的一个重大更新,它引入了一系列的新特性和改进,旨在提高开发效率、提升性能以及增强平台的可靠性。以下是对J2SE 5.0新...
为了更好地理解 J2SE 5.0 中的重要语言特性,本文将详细解析这些特性,并探讨它们的实际应用场景。 #### 1.2 准备工作 在深入探讨 J2SE 5.0 的新特性之前,需要确保已经安装了新版 JDK。可以通过访问 [Sun 官方...
J2SE 5.0是这个平台的一个重要里程碑,它引入了许多关键的新特性,提升了Java的效率、可读性和可维护性。本教材源代码正是围绕这一版本展开,对于学习和理解Java编程具有很高的价值。 1. **自动装箱与拆箱**:J2SE ...
J2SE 5.0引入了泛型,这是一个强大的功能,允许在编译时检查类型安全,减少类型转换的需要。泛型使程序员能够创建可重用的容器类,如ArrayList和HashMap,它们可以安全地存储特定类型的对象,避免了运行时的...
J2SE 5.0的文档包含了关于语言增强、泛型、枚举、变量作用域、类型推断、可变参数等新特性的说明。 1. **语言增强**:J2SE 5.0引入了注解(Annotations),这是一种元数据,可以附加到代码的不同部分,帮助编译器、...
- **新特性**:J2SE 5.0引入了一系列新特性,包括泛型(Generics)、枚举(Enums)、可变参数(Varargs)、增强的for循环等,这些特性极大地简化了代码编写,提高了代码的可读性和维护性。 - **安全性提升**:增强了...
1. **泛型**:J2SE 5.0引入了泛型,允许在定义类、接口和方法时指定类型参数,从而增强了类型安全性和代码重用。泛型可以避免运行时类型转换异常,提高了代码的可读性和可维护性。 2. **自动装箱与拆箱**:Java 5中...
本书是Java技术经典参考书,多年畅销不衰,第7版在保留以前版本风格的基础上,涵盖Java 2开发平台标准版J2SE 5.0的基础知识,主要内容包括面向对象程序设计、反射与代理、接口与内部类、事件监听器模型、使用......
本书是Java技术经典参考书,多年畅销不衰,第7版在保留以前版本风格的基础上,涵盖Java 2开发平台标准版J2SE 5.0的基础知识,主要内容包括面向对象程序设计、反射与代理、接口与内部类、事件监听器模型、使用......
本书是Java技术经典参考书,多年畅销不衰,第7版在保留以前版本风格的基础上,涵盖Java 2开发平台标准版J2SE 5.0的基础知识,主要内容包括面向对象程序设计、反射与代理、接口与内部类、事件监听器模型、使用......
本书是Java技术经典参考书,多年畅销不衰,第7版在保留以前版本风格的基础上,涵盖Java 2开发平台标准版J2SE 5.0的基础知识,主要内容包括面向对象程序设计、反射与代理、接口与内部类、事件监听器模型、使用......
J2SE(Java 2 Platform, Standard Edition)是JavaSE早期的称呼,5.0是其一个重要的版本发布,它在Java发展历程中扮演了关键的角色,引入了许多新特性并优化了已有的功能。 在JavaSE 5.0(也称为Java 5.0)中,主要...
1. **泛型**:J2SE 5.0引入了泛型,允许在定义集合类和其他容器时指定元素类型,从而在编译时就能检查类型安全,避免了类型转换的麻烦和潜在的ClassCastException。 2. **自动装箱与拆箱**:在J2SE 5中,原始类型...
3. **集合框架**:J2SE 5.0中的集合框架是一个强大的工具集,包括List、Set、Map接口,以及ArrayList、LinkedList、HashSet、HashMap等实现类。书中可能会详细讲解它们的使用方法和场景选择。 4. **泛型**:J2SE ...
"j2se5.0CN.chm"文件很可能是Java 2 Standard Edition(J2SE)5.0的中文帮助文件,以CHM(Compiled Help Manual)格式呈现。CHM是一种Windows平台下常见的帮助文档格式,它集成了HTML文档并提供了索引和搜索功能。在...
J2SE 1.5中文API为中国的开发者提供了方便,它详细地阐述了这个版本中的各种类库、接口、方法和异常,是理解和使用Java 5.0核心功能的重要参考资料。 一、泛型(Generics) J2SE 1.5引入了泛型,这是一种类型安全...