Java基础教程:泛型基础

引入泛型

传统编写的限制:

  在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型。如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚很多!

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

1.多态:将方法的参数类型设为基类,那么该方法就可以接收从这个基类导出的任何类作为参数。

class Primary{} //定义基类

class Test()
{
public void f(Primary p)
{...}
}

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

interface Primary{} //定义接口

class Test()
{
public void f(Primary p) //实现了该接口的所有类都可以作为参数
{...}
}

3.使用泛型。

泛型的说明

  泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。

  泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是说在泛型使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称为泛型类、泛型接口、泛型方法。

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

简单泛型

使用泛型预定义参数类型

  

说明:基本数据类型无法作为类型参数

在使用时具体化类型参数

  也就是在声明的时候先不用去管具体的类型,用一个参数代替,在使用的时候,这个参数需要具体化。

  

小结

  泛型类最主要的使用是应用在集合中,代码封装了一个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。

说明:

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

  这意味者当你在使用泛型的时候,任何具体的类型信息都被擦除了,你唯一知道的就是你在使用一个对象。因此问题中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”

  使用方法和解释同上。

泛型表达式的翻译

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

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

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

Java:泛型基础的更多相关文章

  1. 一个小栗子聊聊JAVA泛型基础

    背景 周五本该是愉快的,可是今天花了一个早上查问题,为什么要花一个早上?我把原因总结为两点: 日志信息严重丢失,茫茫代码毫无头绪. 对泛型的认识不够,导致代码出现了BUG. 第一个原因可以通过以后编码 ...

  2. java泛型基础

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 这种参数类型可以用在类.接口和方法的创建中, 分别称为泛型类.泛型接口.泛型方法.  Ja ...

  3. java 泛型基础问题汇总

    泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数.这种参数类型可以用在类.接口和方法的创建中,分别称为泛型类.泛型接口.泛型方法. Java语言引 ...

  4. 黑马----JAVA泛型基础

    黑马程序员:Java培训.Android培训.iOS培训..Net培训 JAVA范型-基础 一.泛型的概念 1.实现了参数化类型 2.用于编写可应用于多种类型的代码,即所编写的代码可应用于许多许多的类 ...

  5. java泛型基础、子类泛型不能转换成父类泛型

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  6. java泛型基础、子类泛型不能转换成父类泛型--未完待续

    参考http://how2j.cn/k/generic/generic-generic/373.html 1.使用泛型的好处:泛型的用法是在容器后面添加<Type>Type可以是类,抽象类 ...

  7. 使用java泛型设计通用方法

    泛型是Java SE 1.5的新特性, 泛型的本质是参数化类型, 也就是说所操作的数据类型被指定为一个参数. 因此我们可以利用泛型和反射来设计一些通用方法. 现在有2张表, 一张user表和一张stu ...

  8. Java基础教程:泛型基础

    Java基础教程:泛型基础 引入泛型 传统编写的限制: 在Java中一般的类和方法,只能使用具体的类型,要么是基本数据类型,要么是自定义类型.如果要编写可以应用于多种类型的代码,这种刻板的限制就会束缚 ...

  9. Java基础学习总结(83)——Java泛型总结

    1. 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型 ...

随机推荐

  1. webpack之傻瓜式教程

    接触webpack也有挺长一段时间了,公司的项目也是一直用着webpack在打包处理,但前几天在教新人的情况下,遇到了一个问题,那就是:尽管网上的webpack教程满天飞,但是却很难找到一个能让新人快 ...

  2. Sublime Text3配置在可交互环境下运行python快捷键

    安装插件 在Sublime Text3下面写代码感觉很不错,但是写Python的时候遇到了一些问题. 用Sublime Text3打开python文件,或者在Sublime Text3下写好pytho ...

  3. Java基础Map接口+Collections工具类

    1.Map中我们主要讲两个接口 HashMap  与   LinkedHashMap (1)其中LinkedHashMap是有序的  怎么存怎么取出来 我们讲一下Map的增删改查功能: /* * Ma ...

  4. C++的内存泄漏检测

    C++大量的手动分配.回收内存是存在风险的,也许一个函数中一小块内存泄漏被重复放大之后,最后掏空内存. 这里介绍一种在debug模式下测试内存泄漏的方法. 首先在文件的开头以确定的顺序写下这段代码: ...

  5. [数据结构]——链表(list)、队列(queue)和栈(stack)

    在前面几篇博文中曾经提到链表(list).队列(queue)和(stack),为了更加系统化,这里统一介绍着三种数据结构及相应实现. 1)链表 首先回想一下基本的数据类型,当需要存储多个相同类型的数据 ...

  6. 一些关于Linux入侵应急响应的碎碎念

    近半年做了很多应急响应项目,针对黑客入侵.但疲于没有时间来总结一些常用的东西,寄希望用这篇博文分享一些安全工程师在处理应急响应时常见的套路,因为方面众多可能有些杂碎. 个人认为入侵响应的核心无外乎四个 ...

  7. A*算法应用[转]

    转自:http://www.cnblogs.com/zhoug2020/p/3468167.html 这是一篇十分精彩/易懂的博客,感谢原博主!本文通过自己的理解在原博文基础上突出一些重点字眼,句子. ...

  8. Windows Server 2008 R2 下配置TLS1.2,添加自签名证书

    前言 2017年1月1日起App Store上的所有App应用将强制开启ATS功能. 苹果的ATS(App Transport Security)对服务器硬性3点要求: ① ATS要求TLS1.2或者 ...

  9. Struts2.5需要的最少jar文件

    以Struts2.5.2为例 从官网上下载“struts-2.5.2-min-lib.zip”,里面有7个jar文件: commons-fileupload-1.3.2.jarcommons-io-2 ...

  10. Ognl表达式基本原理和使用方法

    Ognl表达式基本原理和使用方法 1.Ognl表达式语言 1.1.概述 OGNL表达式 OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个 ...