首页 > 代码库 > 关于泛型的基本使用
关于泛型的基本使用
学习集合框架相关内容之前还是要把泛型好好看下,要不各种源代码看得就很难受了,一遇到<? ><T> 这样的一些表述就头大了,这部分可结合着集合的相关内容一起了解。
泛型基本概念(Genetics)
就像圣思园视频里讲的,用一句比较好的话解释就是:变量类型的参数化。泛型基本思想与C++的模板中的思想比较类似,但是在还有一些区别的比如具体的实现方式上。
使用集合的时候比如按照下面的没有用泛型的方式(其实是<? Extends Object>):
List list=new ArrayList();
List.add(“abc”);list.add(new Integer(1));list.add(new Boolean(true))
String str=(String)list.get(0);Integer a=(Integer)list.get(1);String str2=(String)list.get(2)
上面的例子在编译的时候不会报错,在运行的时候会报错,因为index=2位置上的元素是一个Boolean类型的,这里却要强制转化成一个String类型的。
这就是之前的弊端,因为所有的类全是Object的类型的子类,因此全当做Object类型来处理,但是在取出的时候恢复到原来的类型就遇到了问题,需要进行强制的转换,这样操作虽然可以但是不能保证安全性,就像上面的例子那样。
可以说泛型的一个重要作用就是为了避免强制类型的转换,定义的时候逻辑与之前的相同,涉及到具体的类型信息的时候,用一个符号来代替(所谓类型的参数化),不变应万变。
比如下面一个简单的泛型例子:
package com.test.Genetics;class Genetics<T>{//这里的T 可以看做是表示类型的参数//代码中遇到类型的部分 就用T来替代就好//T看做是一个变量 具体传入的类型只有在运行的时候才能知道public T foo;public T getFoo() {return foo;}public void setFoo(T foo) {this.foo = foo;}}public class testGenetics <T> {public static void main(String[] args) {Genetics<Boolean> foo1=new Genetics<Boolean>();Genetics<Integer> foo2=new Genetics<Integer>();foo1.add("abc");}}
可以看到上面的例子,在实际生成类的实例的时候要预先把类型信息填入其中,这样要是add了其他的类型的实例进去就会报错了,foo1.add(“abc”)这句在编译的时候就会报错,而不会等到运行的时候。实际的集合框架都是通过泛型来实现的,这样在声明的时候就要指定好类型信息,在取出的时候也不用再进行强制的类型转换了。
这个是传入两个泛型参数的例子:
package com.test.Genetics;//这个类里面有两个通过泛型表示的类型变量public class testGeneticb <T1,T2> {private T1 foo1;private T2 foo2;public T1 getFoo1() {return foo1;}public void setFoo1(T1 foo1) {this.foo1 = foo1;}public T2 getFoo2() {return foo2;}public void setFoo2(T2 foo2) {this.foo2 = foo2;}public static void main(String[] args) {testGeneticb<Integer,Boolean> foo=new testGeneticb<Integer,Boolean>();foo.setFoo1(10);foo.setFoo2(new Boolean(false));System.out.println("foo1 is "+foo.getFoo1() +" foo2 is " + foo.getFoo2());}}
下面这个是用泛型实现的一个简单集合,功能比较简单,主要是为了演示对于泛型的操作,注意声明泛型类的构造方法的时候不用加上<T>:
package com.test.Genetics;public class simpleCollection <T>{private T[] objArr;private int index=0;public T[] getObjArr() {return objArr;}public void setObjArr(T[] objArr) {this.objArr = objArr;}public int getIndex() {return index;}public void setIndex(int index) {this.index = index;}//默认参数的时候 数组空间大小为10public simpleCollection(){this.objArr=(T[])new Object[10];}//泛型类的构造方法不用加<>标记了public simpleCollection(int capacity){//注意无法直接创建泛型数组 要先创建Object类型的数组之后再强制转换过来this.objArr=(T[])new Object[capacity];}//这里形参是一个T类型的引用 tpublic void add(T t){this.objArr[index++]=t;}public int getLength(){return this.index;}public T get(int i){return this.objArr[i];}public static void main(String[] args) {int i;simpleCollection<Integer> c=new simpleCollection<Integer>();//存入元素再打印出来for(i=0;i<5;i++){c.add(i);}System.out.println("the length is "+c.getLength());for(i=0;i<5;i++){Integer in=c.get(i);System.out.println(in);}}}
下面这个例子用于演示泛型的嵌套
package com.test.Genetics;class tmpGenetics<T>{private T foo;public T getFoo() {return foo;}public void setFoo(T foo) {this.foo = foo;}}public class wrapperFoo <T>{private tmpGenetics<T> tmpfoo;public tmpGenetics<T> getTmpfoo() {return tmpfoo;}public void setTmpfoo(tmpGenetics<T> tmpfoo) {this.tmpfoo = tmpfoo;}public static void main(String[] args) {tmpGenetics<Integer>foo=new tmpGenetics<Integer>();foo.setFoo(10);wrapperFoo<Integer>wrapper=new wrapperFoo<Integer>();wrapper.setTmpfoo(foo);//将之前存进去的foo对象拿出来 赋给一个新的tmpGenetics<Integer>tmpGenetics<Integer>outfoo=wrapper.getTmpfoo();System.out.println(outfoo.getFoo());}}
下面这个主要演示Map框架中泛型的基本使用以及迭代器的时候泛型的使用(这个可以结合集合框架的那一部分对于Map中的entry的介绍具体来看)
package com.test.Genetics;import java.util.HashMap;import java.util.Iterator;import java.util.Map;import java.util.Map.Entry;import java.util.Set;public class testMap {public static void main(String[] args) {Map<String,String> map=new HashMap<String,String>();map.put("a", "aa");map.put("b", "bb");map.put("c", "cc");map.put("d", "dd");System.out.println("using first kind iterator way");Set<String>keyset=map.keySet();for(Iterator<String>iter=keyset.iterator();iter.hasNext();){String key=iter.next();String value=map.get(key);System.out.println("the key is "+key+"the value is "+value);}//采用第二种方式迭代输出//注意这里的set声明的写法 entry也是一个泛型类(是Map集合中的静态内部类) 其中包含了key value System.out.println("using second kind iterator way");Set<Entry<String, String>>entryset=map.entrySet();for(Iterator<Map.Entry<String, String>>iter=entryset.iterator();iter.hasNext();){Map.Entry<String, String>entry=iter.next();String key=entry.getKey();String value=entry.getValue();System.out.println("the key is "+key+"the value is "+value);}}}
关于限制泛型的作用类型
在实际的开发中要是对泛型的具体实现有限制怎么处理?
比如像下面这种类型声明:
public class Genetics<T extends someclass>这种是固定的语法,其中somclass这个地方表示希望限制的类的名字,即使泛型类在实际声明的时候,<>中只能是这个类及其子类的类型名称。比如<T extends List> 那么生成实例的时候HashMap就不能往<>中放了。
实际上直接声明的<T>相当于<T extends Object>因此在默认的情况下,任何类型都可以作为类型参数,在声明一个泛型类的实例的时候放在<>中。
还有一种情况比如希望声明一个泛型类的引用foo可以实现以下方式:
foo = new Genetics<ArrayList>;
foo = new Genetics<LinkedList>;
即是说foo这个引用不是那种一一对应的关系,声明一个泛型类的引用之后可以将这个引用指向多个泛型类的实例,这就需要用到通配符的方式即
public class Genetics<? extends List> { …}
这样生成引用 Genetics<? extends List> foo之后,foo可以指向类型参数为List或其子类的不同类型的实例。还可以通过<? extends List> 这种方式声明表示继承结构在List上面的,实际中用到的比较少。
注意两种限制方式的区别:
第一种在声明泛型引用的时候就定死了,是一一对应的关系,某种特定类型的引用只能指向对应的特定类型的泛型实例。
第二种是声明引用的时候没有定义好,或者是局部限定范围,是一对多的关系,某种引用可指向限制范围内的多个特定类型的泛型实例。第二种方式仅仅是在声明一个引用的时候才用到的,在声明泛型类的时候还是用之前的方式。
使用通配符的方式要注意的一点是,这种方式意味着只能通过这个引用来取得或者是移除(设为null)实例中的某些信息,但不能增加修改信息,因为只知道这个引用指向的是somclass的子类,但是具体指向的是哪种类型在编译期间并不知道,因此编译器不能让修改操作发生,若是可以发生,就是在编译之前已经知道了具体指向的是哪个类,这个就和泛型的初衷相违背了,比如下面这个例子:
package com.test.Genetics;class tmpGeneticsb<T>{private T str;public T getStr() {return str;}public void setStr(T str) {this.str = str;}}public class testExtends {public static void main(String[] args) {tmpGeneticsb<String>ge=new tmpGeneticsb<String>();ge.setStr("abc");//注意采用通配符的方式 仅仅是在声明指向泛型类的引用的时候才用到的//声明一个泛型类的时候并不能这样使用//<? extends Object>表示了这个引用ge2可以指向所有类型的泛型实例tmpGeneticsb<? extends Object>ge2;ge2=ge;//下面的操作就会无法通过编译//ge2.setStr("cde");}}
关于泛型的基本使用