小言_互联网的博客

Java学习(七)

378人阅读  评论(0)

Java泛型

泛型

  1. 定义:意为参数化类型,即传入的参数是个类型,如集合List<String>,就是一个带类型参数的泛型接口,泛型允许在定义类、接口、方法时使用类型作为形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定。

  2. 语法:Java7之后,在调用对象的构造器的时候,可以省略泛型中的参数类型,而只留下菱形语法,例如

    List<String> books = new ArrayList<>();

  3. 泛型的声明和使用:

    声明:需要指出形参,如:public class Apple<T>

    使用:使用泛型接口、父类时,不能包含类型形参,需要指出具体类型,或者省略不写

    省略不写:

    1. 调用构造器时的菱形语法
    2. 从泛型派生子类时,如果省略菱形:此时T会被当做Object处理
  4. 运行类型:

    不论传入何种类型形参,同一容器的泛型类型实例类型始终相同,即会被当做实例化(运行类型)的非泛型类处理。

  5. 编译类型:

    对编译器来说,List<Object>List<String>没有继承和派生的关系,他们类型也不相等。

  6. 类型通配符

    1. 当需要向定义的方法传递泛型参数时,可以使用类型通配符?作为类型形参。

      示例

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

      此时,传入的类型形参会在方法体内被当作Object处理。

    2. 类型通配符的上限

      如果需要限定类型的范围,可以使用被限制的类型通配符,如:List<? extends Father>,能够接收所有Father类的派生类和他自己,并被编译器默认当做Father类对象

      特别地,向集合等容器添加元素时,必须是能让编译器知道的确切类型(确切的编译类型,使用类型通配符无法添加不确定的子类元素)才能添加。

    3. 类型形参的上限

      在定义类、接口、方法时,也可以采用和通配符限制类似的写法如:public class Apple<T extends Number>,来限制类型形参只能是某一类的派生或者它本身。设置多个上限时,可以这样写:

      public class Apple<T extends Number & java.io.Serializable>

    4. 类型通配符的下限

      类似通配符上限,<? super Type>,表示它必须是Type或者Type的父类。

      应用:TreeSet<E>,(TreeMap类似)他有个构造器是TreeSet(Comparator<? super E> c),其中接口Comparator定义及举例如下:

      //定义
      public interface Comparator<T>
      {
      	int compare(T fst, T snd);
      }
      
      //举例
      public class TreeSetTest
      {
          public static void main(String[] args)
          {
              //以Object为形参比较
              TreeSet<String> ts1 = new TreeSet<>(
                  new Comparator<Object>()
                  {
                      public int compare(Object fst, Object snd)
                      {
                          return fst.hashCode() > snd.hashCode() ? 1
                              : fst.hashCode() < snd.hashCode() ? -1 : 0; 
                      }
                  }
              );
              //以String为形参做比较
              TreeSet<String> ts2 = new TreeSet<>(
                  new Comparator<String>()
                  {
                      public int compare(String first, String second)
                      {
                          return first.length() > second.length() ? -1
                              : first.length() < second.length() ? 1 : 0; 
                      }
                  }
              );
          }
      }
      
  7. 泛型方法

    由于当函数参数带通配符时,无法向集合中添加不确定类型的元素这一缺陷,引入泛型方法。

    即,在方法签名(方法名称和参数类型)中显式声明类型形参。

    格式如下

    public static <T, S> void from ArrayToCollection(T[] a, Collection<T> c)
    {//此处的S表示可以添加不止一个类型形参
        for (T o : a)
        {
            c.add(o);
        }
    }
    

    和通配符的区别:

    1. 如果类型之间有依赖关系(比如方法体内的其他变量类型依赖于传入的类型形参的类型),那么应该使用泛型方法;

      如果仅仅只是在不同的调用点传入不同的实际类型,而类型之间没有相互依赖的关系,则应该使用通配符,但要保证目标集合的元素类型是添加的元素类型的父类。

    2. 类型通配符既可以在方法签名中定义形参类型,也可以用于定义变量类型,而泛型方法必须在方法签名中显式声明,才能在方法体中使用。

    泛型方法重载:

    如果使用了设置上下限的声明,那么这两个声明不能有交集,即使定义时不会出错,调用方法时则会由于具体类型不明,无法确定具体的方法而报错。

  8. 泛型构造器

    Java7以后,可以将构造器写为泛型方法:

    class Foo<E>
    {
    	public <T> Foo(T t)
        {
            System.out.println(t);
        }
    }
    .......
    public static void main(String args[])
    {
        Foo<String> f = new <Integer> Foo<String>(5);
        //得到的t是Integer类型
        //此处String指定的是Foo类声明中的E形参,故泛型构造器在调用的时候,其类型形参的值取决于前面的值
        //T形参为Integer
        //此处由于显示指明了泛型构造器中生命的类型形参为Integer,故无法使用“菱形”语法
    }
    
  9. 擦除与转换

    擦除

    如果不为泛型类指定实际的参数类型,那么该类型参数被称为raw type,默认为第一个上限类型(没指定就是Object),比如List<String>类型转换为List类型,丢失类型参数信息,那么上限将变为Object

  10. 泛型数组

    泛型设计原则——若编译未提出“[unchecked]未经检查的转换警告”,那么运行时就不应该引发ClassCastException警告,所以数组元素类型不能包含类型变量或类型形参,应该使用容器嵌套或者自定义一个新的类来实现相应的功能。

    如果允许这么做,下面的代码就会出问题

    List<String>[] lsa = new List<String>[10]; // illegal
    Object[] oa = lsa;  // OK because List<String> is a subtype of Object
    List<Integer> li = new ArrayList<Integer>();
    li.add(new Integer(3));
    oa[0] = li; 
    String s = lsa[0].get(0);  //error! ClassCastException
    

    但java允许定义无上限的通配符泛型数组,即如下的代码编译可以通过,但是运行时会抛出异常

    List<?>[] lsa = new ArrayList<?>[10]; 
    Object[] oa = lsa;  
    List<Integer> li = new ArrayList<Integer>();
    li.add(new Integer(3));
    oa[0] = li; 
    String s = (String)lsa[0].get(0);  //error! ClassCastException
    

    此外,创建元素类型是类型变量的数组对象也无法编译:

    <T> T[] makeArray(Collection<T> coll)
    {
        return new T[coll.size()];//此处会编译错误
    }
    

转载:https://blog.csdn.net/deltapluskai/article/details/102489640
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场