zoukankan      html  css  js  c++  java
  • thinking in java笔记 15 泛型

    ***概述
       泛型实现了参数化类型的概念,使代码可以应用于多种类型。泛型的最初目的是为了使类或方法具有最广泛的表达能力,这点可以通过解耦类或方法与所使用的类型之间的约束来实现。在创建参数化类型的一个实例时,编译器为你负责转型操作,并且保证类型的正确性。

    ***简单泛型
       许多原因促使泛型的出现,最显著的一个是为了创造容器类。有时,需要能同时持有多个对象。但通常只会使用容器来存储一种类型的对象。泛型的主要目的之一就是指定容器要持有的对象类型,有编译器来保证类型的正确性。

    ***元组
       一次方法调用就能返回多个对象,经常会需要类似的功能。解决办法是创建一个对象,用它来持有想要返回的多个对象。元组是将一组对象直接打包存储与其中的一个单一对象,这个容器对象内元素是只读的。

    ***泛型接口
       泛型可以用于接口,和类使用方法相同。java的泛型局限:基本类型无法作为类型参数。 

    ***泛型方法
       是否拥有泛型方法,与其所在的类是否为泛型没有关系。泛型方法使得该方法能够独立于类而产生变化。应该尽量使用泛型方法而不是泛型类,这样更容易明白。
       定义泛型方法,只需将泛型参数列表置于返回值之前:
        <T> void kk(T t){
                System. out .println(t);
          }
       使用泛型类时,必须在创建对象时指定类型参数的值,而泛型方法则不用指定,编译器会找出具体类型(类型参数推断)。使用泛型方法时和使用普通方法一样,如果调用kk()时传入基本类型,自动打包机制就会介入,将基本类型的值包装为对应的对象。泛型方法和自动打包机制避免了以前我们不得不写的代码。类型推断只对赋值操作有效,其他时候并不起作用。
       在泛型方法中,可以显式地指明类型。 f.<Person>getPerson()

    ***类型信息丢失
       在泛型代码内部,无法获得任何有关泛型参数类型的信息。java泛型是使用擦除来实现的,在使用泛型时,任何具体的类型信息都被擦除,仅知道正在使用一个对象。List<String>和List<Integer>在运行时事实上是相同的类型。
       只有当你希望使用的类型参数比某个具体类型(以及它的所有子类型)更加泛化时(希望代码能够跨多个类工作),使用泛型才有所帮助。

    ***创建数组
       在泛型中创建数组,推荐使用Array.newInstance()方式。
       不能创建泛型数组,解决方案是使用ArrayList.将继续获得数组的行为,以及由泛型提供的编译期的类型安全。成功创建泛型数组的唯一方式是创建一个被擦除类型的新数组,然后对其转型。因为有了擦除,数组的运行时类型就只能是Object[]。
       为了弥补擦除,可以在构造器中传递一个类型标记,以便从擦除中恢复,使得我们可以创建需要的实际类型的数组。转型中产生的警告必须用@Suppress Warnings来压制住,一旦获得了实际类型,就可以返回它,并获得想要的结果,如下:
      
       public class GenericArrayWithTypeToken<T> {
    private T[] array;
    @SuppressWarnings("unchecked")
    public GenericArrayWithTypeToken(Class<T> type, int sz) {
    array = (T[])Array.newInstance(type, sz);
    }
    public void put( int index, T item) {
    array[index] = item;
    }
    public T get( int index) { return array[index]; }
    // Expose the underlying representation:
    public T[] rep() { return array; }
    public static void main(String[] args) {
    GenericArrayWithTypeToken<Integer> gai =
    new GenericArrayWithTypeToken<Integer>(
    Integer. class , 10);
    // This now works:
    Integer[] ia = gai.rep();
    }
    }

    Number[] numbers = new Integer[3];
    numbers[0] = new Integer(0);
    numbers[1] = new Integer(1);
    numbers[2] = new Integer(2);

    numbers[1] = new Double(3.4); //能通过编译,但运行时会报ArrayStoreException


    ***边界
       可以用于在泛型的参数类型上设置限制条件,以强制规定泛型可以应用的类型,更重要的效果是你可以按照自己的边界类型来调用方法。因为擦除移除了类型信息,所以,可以用无界泛型参数调用的方法只是那些可以用Object调用的方法,但如果能将这个参数限制为某个类型子集,就可以用这些类型子集来调用方法。java泛型因此重用了extends关键字。

    ***通配符
           class Fruit {}
    class Apple extends Fruit {}
    class Jonathan extends Apple {}
    class Orange extends Fruit {}

    List<Fruit> fruit= new ArrayList<Apple>();//报错,不能把一个涉及Apple的泛型赋给一个涉及Fruit的泛型
    List<? extends Fruit> fruit= new ArrayList<Apple>();
    用通配符后,List<? extends Fruit>代表具有任何从Fruit机场的类型的列表。用这个List只能调用Fruit的方法。


    ***无界通配符
       <?> 声明:想要Java的泛型来编写这段代码。并非是使用原生类型,但在此处,泛型参数可以持有任何类型。
       当处理多个参数时,又是允许一个参数可以是任何类型,同时为其他参数确定某种特定类型。
       使用确切类型替代通配符类型的好处是:可以用泛型参数来做更多的事,但使用通配符使得必须接受范围更宽的参数化类型作为参数。
       
    ***问题
       1 任何基本类型都不能作为类型参数
          可使用基本类型的包装器类和自动包装机制(自动包装机制不能应用于数组)。当考虑性能时,可使用专门适配基本类型的容器版本。            
  • 相关阅读:
    Codeforces 691A Fashion in Berland
    HDU 5741 Helter Skelter
    HDU 5735 Born Slippy
    HDU 5739 Fantasia
    HDU 5738 Eureka
    HDU 5734 Acperience
    HDU 5742 It's All In The Mind
    POJ Euro Efficiency 1252
    AtCoder Beginner Contest 067 C
    AtCoder Beginner Contest 067 D
  • 原文地址:https://www.cnblogs.com/myparamita/p/2203984.html
Copyright © 2011-2022 走看看