现在的位置: 首页 > 综合 > 正文

使用Java中的泛型

2014年02月10日 ⁄ 综合 ⁄ 共 4697字 ⁄ 字号 评论关闭

1.定义泛型接口和类

  泛型接口:

public interface List<E>
{
	void add(E x);
	Iterator<E> iterator();
	...
}
public interface Iterator<E>
{
	E next();
	boolean hasNext();
	...
}
public interface Map<K,V>
{
	Set<K,V> keySet();
	V put(K key,V value);
	...
}

  使用泛型的类:

import java.util.*;
class Genericclass<T>
{
	private T info;
	public Genericclass(){}
	public Genericclass(T t)
	{//使用泛型的构造器
		this.info=t;
	}
	public void setinfo(T info)
	{
		this.info=info;
	}
	public T getinfo()
	{
		return this.info;
	}
	public static void main(String[] args) 
	{
		//调用使用泛型的构造器
		Genericclass<String> gs=new Genericclass<>("123");
		System.out.println(gs.getinfo());
		Genericclass<Double> gd=new Genericclass<>(3.45);
		System.out.println(gd.getinfo());
	}
}

2.从泛型类派生子类

import java.util.*;
class Genericclass<T>
{
	private T info;
	public Genericclass(){}
	public Genericclass(T t)
	{//使用泛型的构造器
		this.info=t;
	}
	public void setinfo(T info)
	{
		this.info=info;
	}
	public T getinfo()
	{
		return this.info;
	}
	
}
/*定义Genericclass的子类
  使用public class A1 extends Genericclass<String>
  或者public class A1 extends Genericclass方式,不能使用
  public class A1 extends Genericclass<T>,即必须指定具体的类型或者什么都不指定
*/
public class A1 extends Genericclass<String>
{
	public A1(String s)
	{
		super(s);
	}
	public String getinfo()
	{
		return "子类:"+super.getinfo();
	}
	public static void main(String[] args) 
	{
		//调用使用泛型的构造器
		A1 a1=new A1("123");
		System.out.println(a1.getinfo());
	}
}

3.类型通配符

  3.1为什么使用类型通配符?如下:

import java.util.*;
class ArrErr 
{
	public void test(List c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}
    /*public void test(List<Object> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}*/
	public static void main(String[] args) 
	{
		List<String> strList=new ArrayList<>();
		ArrErr ae=new ArrErr();
		/*调用第一个test时不会有编译问题,但调用第二个时会产生编译错误
		  由于List<String>不是List<Object>的子类,二者不能进行转换
		  而一般情况下List是泛型接口,使用时为了能够知道其中元素的类型
		  往往使用第二种方法定义,这就限制了第二种方法的使用,必须完全转为Object
		  类型才能使用
		*/
		ae.test(strList);
		System.out.println("Hello World!");
	}
}

  3.2使用类型通配符

    通过?符号作为参数进行传递,即可以与任何类型进行匹配的类型。则上述的test函数可改为:

public void test(List<?> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}
//这种定义只能访问c中的元素,不能修改和添加
List<?> c= new ArrayList<String>();

因为此时不知道c集合中元素的类型,只表明它是各种泛型List的父类,即可以将List<String>类型的参数传递给List<?>类型的参数。

一般通配符是和泛型结合使用,如下(使用通配符的上限):

public void test(List<? extends E> c)
	{
		for(int i=0;i<c.size();i++)
		{
			System.out.println(c.get(i));
		}
	}

传递给该函数的参数是泛型参数E的子类,或者在泛型方法中使用。

4.泛型方法

  4.1为什么使用泛型方法?如下:

import java.util.*;
class GenMethod 
{
	/*将一个Object数组的元素添加到Collection集合中
	static void fromarraytocollection(Object[] a,Collection<Object> c)
	{
		for(Object o:a)
		{
			c.add(o);
		}
	}*/
	//使用泛型方法定义
	static <T> void  fromarraytocollection(T[] a,Collection<T> c)
	{
		for(T o:a)
		{
			c.add(o);
		}
	}
	public static void main(String[] args) 
	{
		String[] str={"a","b"};
		List<String> slist=new ArrayList<>();
		GenMethod gm=new GenMethod();
		/*使用第一个方法时,下面将会出现编译错误,因为List<String>不是
		  Collection<Object>的子类,不能进行转换也不能使用通配符?,
		  因为需要向c中添加元素,?不知道元素的类型
		*/
		fromarraytocollection(str,slist);
		Object[] obj=new Object[100];
		List<Object> lo=new ArrayList<>();
		fromarraytocollection(obj,lo);
	}
}

  4.2泛型方法的定义方式:修饰符 <T,S> 返回值类型  方法名(形参列表){}。

       大多数情况下可以使用泛型方法来代替类型通配符,如下:

public interface Collection<E>
{
	boolean containsAll(Collection<?> c);
	boolean addAll(Collection<? extends E> c);
	...
}
//转为泛型方法为:
public interface Collection<E>
{
	<T> boolean containsAll(Collection<T> c);
	<T extends E> boolean addAll(Collection<T> c);
	...
}

  注意:如果一个方法的类型形参(a)的类型或返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符,因为如果b的类型不确定,则a的类型也不确定,只能使用类型形参,即泛型方法。

  4.3设定通配符的下限

    为什么要设置下限?如下:

public static<T> T copy(Collection<T> dest,Collection<? extends T> src)
	{
		T last=null;
		for(T t:src)
		{
			last=t;
			dest.add(last);
		}
		/*返回一个目标集合类型的元素,但实际传递的元素类型为T的子类,此时就会忘记原集合的类型
		  而src原类型必须是T的子类,所以需要使用<? extends T>的方法定义
		*/
		return last;
	}
	//使用下述代码访问时,则会出现编译错误
	List<Number> lm=new ArrayList<>();
	List<Integer> li=new ArrayList<>();
	Integer last=copy(lm,li);//此时想反悔Integer类型的参数,但函数中返回的是Number类型

    则只需将上述方法改为:public static<T> T copy(Collection<? super T> dest,Collection<T> src)

    即将dest类型使用通配符表示,设置其下限必须是T类型,及其父类类型。

   注:泛型方法可进行方法重载,如:

  public static<T> void copy(Collection<T> dest,Collection<? extends T> src);
  public static<T> T void copy(Collection<? super T> dest,Collection<T> src);

5.擦出和转换

  当把一个具有泛型信息的对象赋值给另一个没有泛型信息的变量时,所有尖括号之间的类型信息都被扔到,比如把一个List<String>类型转为List,则该List集合元素的类型都转为类型变量的上限,无法进行还原。比如:

 A<Integer> a=new A<>(5);
    Integer as=a.get();//获取5的值
    A b=a;
       Object ob=b.get();//此时b只知道a中的5被转为Object类型
    //无法再将b中的5隐式转为Integer
    Integer ib=b.get();
但下面的这种方式是正确的:
    List<Integer> li=new ArrayList<>();
    List l=li;
    List<String> ls=l;//可以转换,但当访问ls中的数组时,会出现运行时异常

6.泛型和数组

    java5中的泛型有一个重要的原则:如果一段代码在编译时没有提出"[unchecked]"未经检查的异常,则在运行时也不会抛出该异常。  

import java.util.*;
class GenArray 
{
	public static void main(String[] args) 
	{
		下面这句在编译时会引发类型异常
		List<String>[] lsa=new ArrayList[10];//不能使用List<String>[] lsa==new List<String>[10],编译时不会引发异常,运行时引发异常,未被java设计原则
		Object[] oa =(Object[])lsa;
		List<Integer> li=new ArrayList<Integer>();
		li.add(new Integer(3));
		oa[1]=li;
		String s=lsa[1].get(0);//在运行时将会引发类型异常,不能讲Object再转为String
		
		//可将第一句改为如下形式
		List<?>[] lsa =new ArrayList<?>[10];
		//在进行转换时使用下述方法,则不会出现任何错误
		Object target=lsa[1].get(0);
		if(target instanceof String)
		{
			String s=(String)target;
		}
		
	}
}

  

  

抱歉!评论已关闭.