使用Java中的泛型

来源:互联网 发布:公民网络身份识别系统 编辑:程序博客网 时间:2024/06/07 15:15

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;}}}



  




  

0 0