Java:泛型基础

泛型

引入泛型

传统编写的限制:

  在Java中通常的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型。若是要编写能够应用于多种类型的代码,这种刻板的限制就会束缚不少!java

解决这种限制的三种方法:

1.多态:将方法的参数类型设为基类,那么该方法就能够接收从这个基类导出的任何类做为参数。编程

复制代码
class Primary{} //定义基类

class Test()
{
    public void f(Primary p)
    {...}
}
复制代码

2.方法的参数使用接口:任何实现了该接口的类均可以知足该方法。app

复制代码
interface Primary{} //定义接口

class Test()
{
    public void f(Primary p) //实现了该接口的全部类均可以做为参数
    {...}
}
复制代码

3.使用泛型。dom

泛型的说明:

  泛型实现了参数化类型的概念,使代码能够应用于某种不具体的类,而不是具体的一个类或者接口。
简单说就是使代码能够适用于普遍的类型。spa

泛型表达式的翻译(最后看):

  1.当程序调用泛型方法时,若是擦除了泛型返回类型,编译器插入类型转换。翻译

 Pair<Employee> buddies = ...
 Employee buddy = buddies.getFirst();
    • 擦除getFirst的返回类型后将返回Object类型,编译器自动插入Employee的强制类型转换。

    • 也就是说,编译其把这个方法调用翻译为两条虚拟指令:
      ◇ 对原始方法Pair.getFirest的调用。
      ◇ 将返回的Object类型,强制转换为Employee类型。
  2.当存取一个泛型域时也要插入强制类型转换。
    假设 Pair 类的first 域 和 second 域都是 公有的(这不是种好的编程风格, 但在java语法中,这是合法的)。 
    表达式: Employee buddy = buddies.first; 也会在结果字节码中插入强制类型转换;  3d

简单泛型:

使用泛型预约义参数类型:rest

  

说明:基本数据类型没法做为类型参数code

在使用时具体化类型参数对象

  

小结

  泛型类最主要的使用是应用在集合中,代码封装了一个ArrayList,这样咱们在编写类的时候,就不会受限于具体的类,由于具体使用的类型是在初始化对象的时候才指定的。
咱们为何要这样呢?若是这个实现类不用泛型,若是处理多种类型数据的时候,就要编写多个实现类,来针对处理。

元组:

简单元组实例:

  

经过继承机制来实现长度更长的元组

  

泛型接口

说明:

  泛型也能够应用于接口,类和接口的类型参数应该保持一致,都是T或者其余。

  泛型接口最经常使用的一个用法是实现Iterable接口,实现迭代方法!

实例:

复制代码
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.Random;

public class RandomList<T> implements Iterable<T> {
    private ArrayList<T> storage = new ArrayList<T>();
    private Random rand = new Random(new Date().getTime());

    public void add(T item) {
        storage.add(item);
    }

    public T select() {
        return storage.get(rand.nextInt(storage.size()));
    }

    /* 实现Iterable接口 */
    public Iterator<T> iterator() {
        return storage.iterator();
    }
}
复制代码

 

泛型方法

说明:

  是否拥有泛型方法和其是不是泛型类并没有直接关系,也就是说泛型方法能够独立于类而产生变化!

实例:

  

类型说明:

显式的类型说明:

  在泛型方法中,能够显式地指明参数类型,不过不多这样使用!
  

可变参数列表:

   泛型方法和可变参数列表能够很好的共存
  

类型变量的限定

说明:

  1.添加限定来让类型变量具备特定功能。

  

  2.能够有多个限定(接口和类都是能够的),但要注意顺序。

  

为何关键字不用implements呢?毕竟Comparable是一个接口:

  <T extends BoundingType>

  这句代码表示T应该是绑定类型(BoundingType)的子类型。T和绑定类型能够是类,也能够是接口。
  若T是接口,那么方法调用时传进来的是T的实现类也是能够的。extends更接近于子类的概念,因此选用extends。

神秘的擦除

问题:

  
  咱们极可能会认为c1!=c2,结果为false,由于c1不能装入Integer,c2也不能装入String。

说明:

  在泛型代码内部,没法获取任何有关泛型参数类型的信息,也就是你没法得到那个具体的类型是什么,你仅仅能够获知的诸如类型参数标识符和泛型边界这类的信息

Java泛型是使用擦除来实现的:

  这意味者当你在使用泛型的时候,任何具体的类型信息都被擦除了,你惟一知道的就是你在使用一个对象。所以问题中c1==c2,为true。这两种形式都被擦除成为它们的“原生”类型,即List.

原始类型与擦除:

      虚拟机没有泛型类型对象,全部对象都属于普通类。不管什么时候定义了一个泛型类型,都自动提供了一个相应的原始类型,将泛型类型还原成原始类型的过程,称为擦除
  原始类型的名字就是删去类型参数后的泛型类型名。擦除类型变量,并替换为限定类型(无限定类型的变量默认用Object)。

  

    左图的原始类型变为:public class Test<Object>,全部T都变成Object 右图变为:public class Test<Comparable>,全部T都变成Object。   

说明:

  这样就不难理解,为何,加上限定边界后,咱们就能够调用obj.compareTo()了,由于擦除类型变量后,T都被还原成Comparable,效果就是任何实现了Comparable的子类均可以调用Test对象的compare()方法。若是泛型是在Java 1.0就出现的,那么这个特性将不会使用擦除来实现——它将使用具体化,使类型参数保持为第一类的实体,所以你就可以在类型参数上执行基于类型的语言操做和反射操做。

再一次强调:

Test<Cat> test = new Test<Cat>(); 

  看起来class Test应该知道如今工做于Cat之上,而泛型语法也在强烈暗示,在整个类中的各个地方,类型T都在被替换。可是事实并不是如此,不管什么时候,当你在编写这个类的代码时,必须时刻提醒本身:它仅仅只是一个Object
擦除的补偿。

再谈边界

  1.边界使得你能够在用于泛型的类型参数上设置限制条件。尽管这使得你能够强制规定泛型能够应用的类型,可是其潜在的一个更重要的效果是你能够按照本身的边界类型来调用方法。
  2.由于擦除移除了类型信息,因此,能够用无界泛型参数调用的方法只能是那些能够用Object调用的方法,那么你就能够用这些类型字节来调用方法。

通配符

  说明:

    1.Java泛型是强制类型检测的,泛型类型的子类型互不相关。

复制代码
    publicclass Test {  
        public static void main(String[] args) throws Exception{  
            List<Integer> listInteger =new ArrayList<Integer>();  
            List<String> listString =new ArrayList<String>();  
            printCollection(listInteger);  
            printCollection(listString);      
        }   
        public static void printCollection(Collection<Object> collection){  
                   for(Object obj:collection){  
                System.out.println(obj);  
            }    
        }  
    }  

//Integer String都是Object的子类型,可是结果会报错,这就说明了泛型不考虑继承关系
The method printCollection(Collection<Object>) in the type GernericTest is not applicable for the arguments (List<Integer>)
复制代码

  2.咱们但愿泛型能向普通类那样具备面向对象的一些特征:

    • 向上转型为一个泛型对象。
    • 向下转型为一个泛型对象。

  3.为了使泛型能具备面向对象的一些继承关系,Java引入了通配符的一些概念:无界通配符 ?

  为了使泛型的子类型仍然具备相关性,能够直接使用无界通配符:

  

  使用通配符上界“? extends T”,来指定继承关系

  

  

说明:

  ? extends T 的本质上的实现是泛型的自动向上转型。

  使用通配符下界“?super T”

  使用方法和解释同上。

相关文章
相关标签/搜索