首页 > 其他 > 详细

关于泛型的基本使用

时间:2014-10-05 23:06:29      阅读:341      评论:0      收藏:0      [点我收藏+]

    学习集合框架相关内容之前还是要把泛型好好看下,要不各种源代码看得就很难受了,一遇到<? ><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;

}

//默认参数的时候 数组空间大小为10

public simpleCollection(){

this.objArr=(T[])new Object[10];

}

//泛型类的构造方法不用加<>标记了

public simpleCollection(int capacity)

{

//注意无法直接创建泛型数组 要先创建Object类型的数组之后再强制转换过来

this.objArr=(T[])new Object[capacity];

}

//这里形参是一个T类型的引用 t

public 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");

}

}

关于泛型的基本使用

原文:http://www.cnblogs.com/Goden/p/4007468.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!