黑马程序员——学习笔记16.Java泛型

来源:互联网 发布:智尚网络 编辑:程序博客网 时间:2024/06/10 05:35

----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

 

泛型(Generic)

基本概念

泛型是JDK1.5的新特性内容之一,由于内容较多较杂,不易掌握,单独笔记。泛型就是一般的所有类型。应用在JDK1.5的安全机制,将运行时期的问题ClassCastException转到了编译时期。避免了强制转换的麻烦。当操作的引用数据类型不确定的时候,就使用<>.将操作的引用数据类型传入即可。<>接受的是一个具体引用数据类型的参数范围。在程序中,只要用到了带有<>的 类或者接口,就要明确传入的具体引用数据类型。

泛型在集合上的应用

没有使用泛型时,只要是对象,不管是什么类型的对象,都可以存储进同一个集合中。使用泛型集合,可以将一个集合中的元素限定为一个特定类型,集合中只能存储同一个类型的对象这样更安全;并且当从集合获取一个对象时,编译器也可以知道这个对象的类型,不需要对对象进行强制类型转换,这样更方便。

注意:在JDK 1.5中,你还可以按原来的方式将各种不同类型的数据装到一个非泛型集合中,但编译器会报告unchecked警告。

代码演示:

public static void main(String[] args) {ArrayList<String> list =  new ArrayList<String>();ArrayList<String> list1=  new ArrayList();ArrayList list2  =  new ArrayList<String>();    list.add("a");    list.add(1);//这句编译时就报错,因为1是Integer类型,不是String类型}

上述的ArrayList的集合list,list1,list2就限定了存储集合的类型,只能存储String类型,最后一句代码就会编译报错,这是编译器会检查add的数据类型和自定义的泛型类型参数类型是否一致,不一致就会报错。

泛型的特点

1.泛型技术是给编译器使用的技术,用于编译时期,确保了类型的安全。运行时会将泛型去掉,生产的class文件中是不带有泛型的,这个称为泛型的擦除。

为什么要擦除呢?是为了兼容运行的类加载器。同时泛型也有补偿补偿,在运行时,通过泛型元素的类型进行转换动作,不用使用者再强制转换了。

代码演示:创建一个ArrayList对象,存储的是String类型对象,怎样建Integer类型的对象存入到ArrayList中

public static void addString(String str)throws NoSuchMethodException,SecurityException, IllegalAccessException,IllegalArgumentException, InvocationTargetException{  ArrayList<Integer> list = new ArrayList<Integer>();//通过反射获取这个集合的字节码文件,这时的泛型已经被擦除,没有限定类型Class clazz = list.getClass();Method methodAdd = clazz.getMethod("add", Object.class);  //添加String类型的"abc"methodAdd.invoke(list, "abc");System.out.println(list);}


 

2.参数化类型与原始类型的兼容性:因为泛型是JDK1.5才出现的,为了兼容之前版本的技术,所以参数化类型可以引用一个原始类型的对象,原始类型可以引用一个参数化类型的对象。

例:ArrayList<String> list =  new ArrayList();

   ArrayList list =  new ArrayList<String>();

   List<String> listnew ArrayList();

    List listnew ArrayList<String>();

    编译器不会报错,但警告

3.参数化类型不考虑类型参数的继承关系

ArrayList<String> list =  new ArrayList<Object>();

ArrayList<Object> list =  new ArrayList<String>();

以上两句代码编译时都会报错。

注意:ArrayList list1 = new ArrayList<String>();

      ArrayList<String> list2 = new ArrayList<String>();

      ArrayList<Object> list = list1;

      ArrayList<Object> list = list2;

     上述代码中就只有最后一行代码会报编译错误

4.在声明创建一个数组时,不能声明数组的元素类型是泛型类型,否则编译器会报错。换句话就是创建数组实例时,数组的元素不能使用参数化的类型

下面语句有错误:

ArrayList<String>[]  list =  new ArrayList[2];

应改成:

ArrayList[]  list =  new ArrayList[2];

5.泛型涉及名称术语

例: ArrayList<E>类定义

     ArrayList<Integer> list2 = new ArrayList<Integer>();

     ArrayList称为原始类型

     ArrayList<E>称为泛型类型

     ArrayList<Integer>称为参数化的类型为Integer类型的泛型类型

     ArrayList<E>中的E称为类型变量或类型参数

     ArrayList<Integer>中的Integer称为类型参数的实例或实际类型参数

     ArrayList<Integer>中的<>念着typeof

6.泛型中的通配符----?

使用?通配符可以引用其他各种参数化的类型,?通配符定义的变量主要用作引用,可以调用与参数化无关的方法,不能调用与参数化有关的方法。

通过下面两个例子来展示:

练习:定义一个方法,该方法用于打印出任意参数化类型的集合中的所有数据,该方法如何定义呢?

错误方式:

public static void printCollection(Collection<Object> cols) {for(Object obj:cols) {System.out.println(obj);}cols.add("string");//正确,因为String类型是Object的子类cols = new HashSet<Date>();//会报告错误,因为泛型不考虑子父类。}

正确方式:

public static void printCollection(Collection<?> cols) {for(Object obj:cols) {System.out.println(obj);}cols.add("string");//这句话会报编译错误。因为它不知自己未来匹配就一定是Stringcols.size();//没错,此方法与类型参数没有关系cols = new HashSet<Date>();//正确。因为?可以是任意类型。}

限定通配符的上边界:

正确:ArrayList<? extends Number> list = new ArrayList<Integer>();

错误:ArrayList<? extends Number> list = new ArrayList<String>();

因为在声明时就将泛型限定在Number类型和其子类类型,String类型与Number类没有继承关系。

限定通配符的下边界:

正确:ArrayList<? super Integer> list = new ArrayList<Number>();

错误:ArrayList<? super Integer> list = new ArrayList<Byte>();

因为在声明时就将泛型限定在Integer类型和其父类类型,String类型与Integer类没有继承关系。

自定义泛型

1.自定义泛型方法

格式:如果传入的参数类型不确定是,就可以定义成泛型,用T表示,但T是什么意思呢?这就需要在返回值类型前声明泛型,用<T>表示。

普通方法、构造方法和静态方法中都可以使用泛型。也可以用类型变量表示异常,称为参数化的异常,可以用于方法的throws列表中,但是不能用于catch子句中。

T可以是Integer,Byte等包装类类型,可以是数组,也可以是自定义引用类型,但不能是基本数据类型。

  修饰符  <T>返回值类型 方法名(T t1,T t2....){

 

  }

代码演示:定义一个方法可以交换数组中两个元素位置

public static <T extends Number> void swap(T[] t,int a,int b){T temp = t[a];t[a] = t[b];t[b] = temp;}//这里就用到了泛型,因为不知道数组中元素的类型,所有就用了泛型。public static void main(String[] args) {int [] arr = {1,35,45,7,65,344};Integer[] arr1  = {1,3,4,5};String[] arr2 = {"itheima","itcast"};swap(arr,0,1);//这个会报错,因为基本数据类型不是泛型参数化的类型。swap(arr1,0,1);swap(arr2,0,1);//这个也报错,因为String不是Number的子类}

练习1:编写一个泛型方法,自动将Object类型的对象转换成其他类型。

 public static <T> T transfer(Object obj){

      return (T)obj;

 }

练习2:定义一个方法,可以将任意类型的数组中的所有元素填充为相应类型的某个对象。

  public static <T> void fill(T[] arr,T obj){

       for(int i = 0;1<arr.length ;i++){

         arr[i] = obj;

       }

  }

练习3:采用自定泛型方法的方式打印出任意参数化类型的集合中的所有内容。

  public static <T> void print(Collection<T> collection){

      for(T t :collection)

          System.out.println(t);

 }

2.自定义泛型java

如果类的实例对象中的多处都要用到同一个泛型参数,即这些地方引用的泛型类型要保持同一个实际类型时,这时候就要采用泛型类型的方式进行定义,也就是类级别的泛型。

注意:如果在类上自定义了泛型,那么在创建对象的时候可以为该自定义泛型指定类型。如果在类上自定义了泛型,创建对象的时候没有给自定义的泛型执行类型的话,那么默认是Object数据类型。在类上自定义的泛型,那么方法上不需要声明泛型,但不会作用于静态方法中,如果静态方法需要使用自定义泛型,那么只能在方法上自定义。当一个变量被声明为泛型时,只能被实例变量、方法和内部类调用,而不能被静态变量和静态方法调用。

格式:

 public class Test<T> {

    private T field;

    public void comparaTo(T obj){}

    public T getName(int id){}

  }

类级别的泛型是根据引用该类名时指定的类型信息来参数化类型变量的,例如,如下两种方式都可以:

Test<String> test = null;

new Test<String>();

3.自定义泛型接口

在接口上自定义泛型的格式:

  interface  接口名<T>{

 

  }

注意:

1.在接口上自定义的泛型,接口的实现子类如果指定接口的自定义泛型的类型,那么覆写的方法的参数类型就是指定的泛型类型。

代码演示:

interface A<T>{public  void  print(T t); }public class B implements A<String>{//指定了接口的自定义泛型的类型为   //String类型  public static void main(String[] args) {B b = new B();  b.print("aaa");  //B<String> b = new B();这句会报编译错误}  //覆写print()方法的参数类型就是定义泛型的类型了  @Override  public void print(String s) {//方法的参数类型就是指定的String类型。  System.out.println(s);}

2.如果实现接口时没有指定接口的自定义泛型的类型,那么该自定义泛型的类型默认是Object类型,

代码演示:

interface A<T>{public  void  print(T t); }public class B implements A{//没有指定接口的自定义泛型的类型  public static void main(String[] args) {B b = new B();  b.print("aaa");}  //覆写print()方法的参数类型就是定义泛型的类型了  @Override  public void print(Object object) {//默认为Object类型  System.out.println(obj);}

3.如果实现接口是没有指定接口的类型,但是想在创建接口实现类的对象时,确定操作的数据类型,那么也必须要在类上声明自定义泛型。被覆写的方法的参数是默认Object类型

代码演示:

interface A<T>{public  void  print(T t); }public class B<T> implements A{//没有指定接口的自定义泛型的类型,但要在  //实现的子类名后面声明自定义泛型  public static void main(String[] args) {B<String> b = new B();//创建对象时确认操作的泛型数据类型  b.print("aaa");}  //覆写print()方法的参数类型就是定义泛型的类型了  @Override    public void print(Object object) {//默认为Object类型  System.out.println(obj);}


 

----------------------ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.csdn.net

0 0