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. 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

    一.前言     DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...

  2. 【开源】分享2011-2015年全国城市历史天气数据库【Sqlite+C#访问程序】

    由于个人研究需要,需要采集天气历史数据,前一篇文章:C#+HtmlAgilityPack+XPath带你采集数据(以采集天气数据为例子),介绍了基本的采集思路和核心代码,经过1个星期的采集,历史数据库 ...

  3. iOS开发之Alamofire源码深度解析

    今天博客中的Alamofire源码的版本是以现在最新的3.4版本为例.上篇博客系统的对NSURLSession相关的东西进行了详细的解析,详情请看<详解NSURLSession>,为了就是 ...

  4. 算法与数据结构(十五) 归并排序(Swift 3.0版)

    上篇博客我们主要聊了堆排序的相关内容,本篇博客,我们就来聊一下归并排序的相关内容.归并排序主要用了分治法的思想,在归并排序中,将我们需要排序的数组进行拆分,将其拆分的足够小.当拆分的数组中只有一个元素 ...

  5. ajax前后端数据交互简析

    前端-------->后端 方法:POST 将要传递给后台的数据在前端拼接成url字符串,通过request.send()传递给后台,后台php把得到的数据以索引数组的方式存储在$_POST中. ...

  6. C#各种同步方法 lock, Monitor,Mutex, Semaphore, Interlocked, ReaderWriterLock,AutoResetEvent, ManualResetEvent

    看下组织结构: System.Object System.MarshalByRefObject System.Threading.WaitHandle System.Threading.Mutex S ...

  7. CommandPattern

    /** * 命令模式 * @author TMAC-J * 将调用者和接受者分离 * 可以将一组命令组合在一起,适合很多命令的时候 */ public class CommandPattern { i ...

  8. 命名sql数据集

    所谓的命名sql其实也就是数据库里的sql语句,普元EOS里做了一定的封装,以方便在程序中的使用. 命名SQL的基本元素包括: 1. <parameterMap> parameterMap ...

  9. SpringMvc中初始化参数绑定

    初始化参数绑定与类型转换很类似,初始化绑定时,主要是参数类型 ---单日期 在处理器类中配置绑定方法  使用@InitBinder注解 在这里首先注册一个用户编辑器 参数一为目标类型   proper ...

  10. .NET Core dotnet 命令大全

    dotnet 命令大全,让你理解dotnet 命令. 本文将以一个实例串起 dotnet 所有命令,让你玩转dotnet 命令. 本篇文章编写环境为windows 10 ,dotnet 命令同样适用于 ...