在已发布的java1.4中在核心代码库中增加了许多新的api(如loging,正则表达式,nio)等,在最新发布的jdk1.5和即将发布的jdk1.6中也新增了许多api,其中比较有重大意义的就是generics(范型)。
一.什么是generics?
generics可以称之为参数类型(parameterized types),由编译器来验证从客户端将一种类型传送给某一对象的机制。如java.util.arraylist,
编译器可以用generics来保证类型安全。
在我们深入了解generics之前,我们先来看一看当前的java 集合框架(collection)。在j2se1.4中所有集合的root interface是collection
collections example without genericity: example 1
1 protected void collectionsexample() {
2 arraylist list = new arraylist();
3 list.add(new string("test string"));
4 list.add(new integer(9)); // purposely placed here to create a runtime classcastexception
5 inspectcollection(list);
6 }
7
8
9 protected void inspectcollection(collection acollection) {
10 iterator i = acollection.iterator();
11 while (i.hasnext()) {
12 string element = (string) i.next();
13 }
14 }以上的样例程序包含的两个方法,collectionexample方法建立了一个简单的集合类型arraylist,并在arraylist中增加了一个string和一个integer对象.而在inspeccollection方法中,我们迭代这个arraylist用string进行cast。我们看第二个方法,就出现了一个问题,collection在内部用的是object,而我们要取出collection中的对象时,需要进行cast,那么开发者必需用实际的类型进行cast,像这种向下造型,编译器无
法进行检查,如此一来我们就要冒在代码在运行抛出classcastexception的危险。我们看inspeccollection方法,编译时没有问题,但在运行时就会抛出classcastexception异常。所以我们一定要远离这个重大的运行时错误
二.使用generics
从上一章节中的casscastexception这种异常,我们期望在代码编译时就能够捕捉到,下面我们使用范型修改上一章的样例程序。
//example 2
1 protected void collectionsexample() {
2 arraylist<string> list = new arraylist<string>();
3 list.add(new string("test string"));
4 // list.add(new integer(9)); this no longer compiles
5 inspectcollection(list);
6 }
7
8
9 protected void inspectcollection(collection<string> acollection) {
10 iterator<string> i = acollection.iterator();
11 while(i.hasnext()) {
12 string element = i.next();
13 }
14 }从上面第2行我们在创建arraylist时使用了新语法,在jdk1.5中所有的collection都加入了generics的声明。例:
//example 3
1 public class arraylist<e> extends abstractlist<e> {
2 // details omitted...
3 public void add(e element) {
4 // details omitted
5 }
6 public iterator<e> iterator() {
7 // details omitted
8 }
9 }这个e是一个类型变量,并没有对它进行具体类型的定义,它只是在定义arraylist时的类型占位符,在example 2中的我们在定义arraylist的实
例时用string绑定在e上,当我们用add(e element)方法向arraylist中增加对象时, 那么就像下面的写法一样: public void add(string element);因为在arraylist所有方法都会用string来替代e,无论是方法的参数还是返回值。这时我们在看example 2中的第四行,编译就会反映出编译错误。
所以在java中增加generics主要的目的是为了增加类型安全。
通过上面的简单的例子我们看到使用generics的好处有:
1.在类型没有变化时,collection是类型安全的。
2.内在的类型转换优于在外部的人工造型。
3.使java 接口更加强壮,因为它增加了类型。
4.类型的匹配错误在编译阶段就可以捕捉到,而不是在代码运行时。
受约束类型变量
虽然许多class被设计成generics,但类型变量可以是受限的
public class c1<t extends number> { }
public class c2<t extends person & comparable> { }
第一个t变量必须继承number,第二个t必须继承person和实现comparable
三.generics 方法
像generics类一样,方法和构造函数也可以有类型参数。方法的参数的返回值都可以有类型参数,进行generics。
//example 4
1 public <t extends comparable> t max(t t1, t t2) {
2 if (t1.compareto(t2) > 0)
3 return t1;
4 else return t2;
5 }这里,max方法的参数类型为单一的t类型,而t类型继承了comparable,max的参数和返回值都有相同的超类。下面的example 5显示了max方法的几个约束。
//example 5
1 integer iresult = max(new integer(100), new integer(200));
2 string sresult = max("aa", "bb");
3 number nresult = max(new integer(100), "aaa"); // does not compile
在example 5第1行参数都为integer,所以返回值也是integer,注意返回值没有进行造型。
在example 5第2行参数都为string,所以返回值也是string,注意返回值没有进行造型。以上都调用了同一个方法。
在example 5第3行产生以下编译错误:
example.java:10: incompatible types
found : java.lang.object&java.io.serializable&java.lang.comparable<?>
required: java.lang.number
number nresult = max(new integer(100), "aaa");
这个错误发生是因为编译器无法确定返回值类型,因为string和integer都有相同的超类object,注意就算我们修正了第三行,这行代码在运行仍然会报错,因为比较了不同的对象。
四.向下兼容
任何一个新的特色在新的jdk版本中出来后,我们首先关心的是如何于以前编写的代码兼容。也就是说我们编写的example 1程序不需要任何的改变就可以运行,但是编译器会给出一个"row type"的警告。在jdk1.4中编写的代码如何在jvm1.5中完全兼容运行,我们要人工进行一个:type erasure处理过程
五.通配符
//example 6
list<string> stringlist = new arraylist<string>(); //1
list<object> objectlist = stringlist ;//2
objectlist .add(new object()); // 3
string s = stringlist .get(0);//4
乍一看,example
6是正确的。但stringlist本意是存放string类型的arraylist,而objectlist中可以存入任何对象,当在第3行进行处理时,stringlist也就无法保证是string类型的arraylist,此时编译器不允许这样的事出现,所以第3行将无法编译。
//example 7
void printcollection(collection<object> c)
{ for (object e : c) {
system.out.println(e);
}}
example 7的本意是打印所有collection的对象,但是正如example 6所说的,编译会报错,此时就可以用通配符“?”来修改example 7
//example 8
void printcollection(collection<?> c)
{ for (object e : c) {
system.out.println(e);
}}
example 8中所有collection类型就可以方便的打印了
有界通配符 <t extends number>(上界) <t super number>(下界)
六.创建自己的范型
以下代码来自http://www.java2s.com/examplecode/language-basics
1.一个参数的generics
//example 9(没有使用范型)
class nongen {
object ob; // ob is now of type object
// pass the constructor a reference to
// an object of type object
nongen(object o) {
ob = o;
}
// return type object.
object getob() {
return ob;
}
// show type of ob.
void showtype() {
system.out.println("type of ob is " +
ob.getclass().getname());
}
}
// demonstrate the non-generic class.
public class nongendemo {
public static void main(string args[]) {
nongen iob;
// create nongen object and store
// an integer in it. autoboxing still occurs.
iob = new nongen(88);
// show the type of data used by iob.
iob.showtype();
// get the value of iob.
// this time, a cast is necessary.
int v = (integer) iob.getob();
system.out.println("value: " + v);
system.out.println();
// create another nongen object and
// store a string in it.
nongen strob = new nongen("non-generics test");
// show the type of data used by strob.
strob.showtype();
// get the value of strob.
// again, notice that a cast is necessary.
string str = (string) strob.getob();
system.out.println("value: " + str);
// this compiles, but is conceptually wrong!
iob = strob;
v = (integer) iob.getob(); // runtime error!
}
} //example 10(使用范型)
class example1<t>{
private t t;
example1(t o){
this.t=o;
}
t getob(){
return t;
}
void showobject(){
system.out.println("对象的类型是:"+t.getclass().getname());
}
}
public class genericsexample1 {
/**
* @param args
*/
public static void main(string[] args) {
// todo auto-generated method stub
example1<integer> examplei=new example1<integer>(100);
examplei.showobject();
system.out.println("对象是:"+examplei.getob());
example1<string> examples=new example1<string>("bill");
examples.showobject();
system.out.println("对象是:"+examples.getob());
}
}我们来看example 9没有使用范型,所以我们需要进行造型,而example 10我们不需要任何的造型
2.二个参数的generics
//example 11
class twogen<t, v> {
t ob1;
v ob2;
// pass the constructor a reference to
// an object of type t.
twogen(t o1, v o2) {
ob1 = o1;
ob2 = o2;
}
// show types of t and v.
void showtypes() {
system.out.println("type of t is " +
ob1.getclass().getname());
system.out.println("type of v is " +
ob2.getclass().getname());
}
t getob1() {
return ob1;
}
v getob2() {
return ob2;
}
}
public class genericsexamplebytwoparam {
/**
* @param args
*/
public static void main(string[] args) {
// todo auto-generated method stub
twogen<integer, string> tgobj =
new twogen<integer, string>(88, "generics");
// show the types.
tgobj.showtypes();
// obtain and show values.
int v = tgobj.getob1();
system.out.println("value: " + v);
string str = tgobj.getob2();
system.out.println("value: " + str);
}
}3.generics的hierarchy
//example 12
class stats<t extends number> {
t[] nums; // array of number or subclass
// pass the constructor a reference to
// an array of type number or subclass.
stats(t[] o) {
nums = o;
}
// return type double in all cases.
double average() {
double sum = 0.0;
for(int i=0; i < nums.length; i++)
sum += nums[i].doublevalue();
return sum / nums.length;
}
}
public class genericsexamplebyhierarchy {
/**
* @param args
*/
public static void main(string[] args) {
// todo auto-generated method stub
integer inums[] = { 1, 2, 3, 4, 5 };
stats<integer> iob = new stats<integer>(inums);
double v = iob.average();
system.out.println("iob average is " + v);
double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
stats<double> dob = new stats<double>(dnums);
double w = dob.average();
system.out.println("dob average is " + w);
// this won't compile because string is not a
// subclass of number.
// string strs[] = { "1", "2", "3", "4", "5" };
// stats<string> strob = new stats<string>(strs);
// double x = strob.average();
// system.out.println("strob average is " + v);
}
} 4.使用通配符
//example 14
class statswildcard<t extends number> {
t[] nums; // array of number or subclass
// pass the constructor a reference to
// an array of type number or subclass.
statswildcard(t[] o) {
nums = o;
}
// return type double in all cases.
double average() {
double sum = 0.0;
for (int i = 0; i < nums.length; i++)
sum += nums[i].doublevalue();
return sum / nums.length;
}
// determine if two averages are the same.
// notice the use of the wildcard.
boolean sameavg(statswildcard<?> ob) {
if (average() == ob.average())
return true;
return false;
}
}
public class genericsexamplebywildcard {
/**
* @param args
*/
public static void main(string[] args) {
// todo auto-generated method stub
integer inums[] = { 1, 2, 3, 4, 5 };
statswildcard<integer> iob = new statswildcard<integer>(inums);
double v = iob.average();
system.out.println("iob average is " + v);
double dnums[] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
statswildcard<double> dob = new statswildcard<double>(dnums);
double w = dob.average();
system.out.println("dob average is " + w);
float fnums[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f };
statswildcard<float> fob = new statswildcard<float>(fnums);
double x = fob.average();
system.out.println("fob average is " + x);
// see which arrays have same average.
system.out.print("averages of iob and dob ");
if (iob.sameavg(dob))
system.out.println("are the same.");
else
system.out.println("differ.");
system.out.print("averages of iob and fob ");
if (iob.sameavg(fob))
system.out.println("are the same.");
else
system.out.println("differ.");
}
}5.使用边界通配符
//example 15
class twod {
int x, y;
twod(int a, int b) {
x = a;
y = b;
}
}
// three-dimensional coordinates.
class threed extends twod {
int z;
threed(int a, int b, int c) {
super(a, b);
z = c;
}
}
// four-dimensional coordinates.
class fourd extends threed {
int t;
fourd(int a, int b, int c, int d) {
super(a, b, c);
t = d;
}
}
// this class holds an array of coordinate objects.
class coords<t extends twod> {
t[] coords;
coords(t[] o) { coords = o; }
}
// demonstrate a bounded wildcard.
public class boundedwildcard {
static void showxy(coords<?> c) {
system.out.println("x y coordinates:");
for(int i=0; i < c.coords.length; i++)
system.out.println(c.coords[i].x + " " +
c.coords[i].y);
system.out.println();
}
static void showxyz(coords<? extends threed> c) {
system.out.println("x y z coordinates:");
for(int i=0; i < c.coords.length; i++)
system.out.println(c.coords[i].x + " " +
c.coords[i].y + " " +
c.coords[i].z);
system.out.println();
}
static void showall(coords<? extends fourd> c) {
system.out.println("x y z t coordinates:");
for(int i=0; i < c.coords.length; i++)
system.out.println(c.coords[i].x + " " +
c.coords[i].y + " " +
c.coords[i].z + " " +
c.coords[i].t);
system.out.println();
}
public static void main(string args[]) {
twod td[] = {
new twod(0, 0),
new twod(7, 9),
new twod(18, 4),
new twod(-1, -23)
};
coords<twod> tdlocs = new coords<twod>(td);
system.out.println("contents of tdlocs.");
showxy(tdlocs); // ok, is a twod
// showxyz(tdlocs); // error, not a threed
// showall(tdlocs); // erorr, not a fourd
// now, create some fourd objects.
fourd fd[] = {
new fourd(1, 2, 3, 4),
new fourd(6, 8, 14, 8),
new fourd(22, 9, 4, 9),
new fourd(3, -2, -23, 17)
};
coords<fourd> fdlocs = new coords<fourd>(fd);
system.out.println("contents of fdlocs.");
// these are all ok.
showxy(fdlocs);
showxyz(fdlocs);
showall(fdlocs);
}
} 6.arraylist的generics
//example 16
public class arraylistgenericdemo {
public static void main(string[] args) {
arraylist<string> data = new arraylist<string>();
data.add("hello");
data.add("goodbye");
// data.add(new date()); this won't compile!
iterator<string> it = data.iterator();
while (it.hasnext()) {
string s = it.next();
system.out.println(s);
}
}
} 7.hashmap的generics
//example 17
public class hashdemogeneric {
public static void main(string[] args) {
hashmap<integer,string> map = new hashmap<integer,string>();
map.put(1, "ian");
map.put(42, "scott");
map.put(123, "somebody else");
string name = map.get(42);
system.out.println(name);
}
} 8.接口的generics
//example 18
interface minmax<t extends comparable<t>> {
t min();
t max();
}
// now, implement minmax
class myclass<t extends comparable<t>> implements minmax<t> {
t[] vals;
myclass(t[] o) { vals = o; }
// return the minimum value in vals.
public t min() {
t v = vals[0];
for(int i=1; i < vals.length; i++)
if(vals[i].compareto(v) < 0) v = vals[i];
return v;
}
// return the maximum value in vals.
public t max() {
t v = vals[0];
for(int i=1; i < vals.length; i++)
if(vals[i].compareto(v) > 0) v = vals[i];
return v;
}
}
public class genifdemo {
public static void main(string args[]) {
integer inums[] = {3, 6, 2, 8, 6 };
character chs[] = {'b', 'r', 'p', 'w' };
myclass<integer> iob = new myclass<integer>(inums);
myclass<character> cob = new myclass<character>(chs);
system.out.println("max value in inums: " + iob.max());
system.out.println("min value in inums: " + iob.min());
system.out.println("max value in chs: " + cob.max());
system.out.println("min value in chs: " + cob.min());
}
}9.exception的generics
//example 20
interface executor<e extends exception> {
void execute() throws e;
}
public class genericexceptiontest {
public static void main(string args[]) {
try {
executor<ioexception> e =
new executor<ioexception>() {
public void execute() throws ioexception
{
// code here that may throw an
// ioexception or a subtype of
// ioexception
}
};
e.execute();
} catch(ioexception ioe) {
system.out.println("ioexception: " + ioe);
ioe.printstacktrace();
}
}
}
闽公网安备 35060202000074号