Java泛型入门
在学习本章之前,须要对Java的集合(Collection、Map)有一定的基础。
Java集合有一个缺点。就是把一个对象“丢进”集合里后,集合就会“忘记”这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型(其执行时类型没变)。
之所以这样设计是由于设计集合的程序猿不会知道我们要用它来保存什么类型的对象,所以这样设计具有非常好的通用性。可是这样做带来例如以下两个问题:
l 集合对与元素类型没有限制,如一个集合能保存一个苹果对象也能保存一个香蕉对象;
l 因为对象进入集合后失去了其状态信息,所以取出来时须要进行强制类型转换。
看以下的程序:
List list
= new ArrayList();
list.add(1);
list.add(2);
list.add("五号");//一不小心插入了String
for (Object
object : list){
//取出“五号”时报ClassCastException
Integert = (Integer)object;
}
上面是一个集合没有使用泛型时出现的错误。那么当我们使用泛型时。该错误就会避免,比如:
List<Integer>list = newArrayList<Integer>();
list.add(1);
list.add(2);
list.add("五号");//插入String,引起编译错误
1、认识泛型
Java的參数化类型被称为泛型,即同意我们在创建集合时就指定集合元素的类型,该集合仅仅能保存其指定类型的元素。
泛型同意在定义类、接口、方法时使用类型形參,这个类型形參将在声明变量、创建对象、调用方法时动态地指定。比如:
//定义一个接口
interface Money<E>{
Eget(intindex);
boolean add(E
e);
}
//定义一个类
public classApple<T>{
private T info;
public Apple(T
info) {
this.info =
info;
}
public T getInfo(){
return this.info;
}
public void setInfo(T
info){
this.info =
info;
}
public static void main(String[]
args) {
Apple<String>ap1 = newApple<String>("小苹果");
System.out.println(ap1.getInfo());
Apple<Double>ap2 = newApple<Double>(1.23);
System.out.println(ap2.getInfo());
}
}
须要注意的是,在静态方法、静态初始化块或者静态变量的声明和初始化中不同意使用类型形參。由于无论为泛型的类型形參传入哪一种类型实參,对于Java来说,它们依旧被当成同一个类处理,在内存中也仅仅占用一块内存空间。无论泛型的实际类型參数是什么。它们在执行时总有相同的类(class),比如以下的程序将输出true。
List<String> l1 = new ArrayList<String>();
List<Double> l2 = new ArrayList<Double>();
System.out.println(l1.getClass() == l2.getClass());
2、类型通配符
假设我们想定义一个方法,这种方法的參数是一个集合形參。可是集合形參的元素类型是不确定的。可能会想到以下两种定义方法:
public void test1(List l){ }
public void test2(List<Object>
l){ }
test1能够使用,可是会报泛型警告;
test2在传入非List<Object>时无法使用。会报“test2(List<Object>)对于參数(List<String>)不适用”;
为了表示各种泛型List的父类,我们须要使用类型通配符。类型通配符是一个问号(?),将一个问号作为类型实參传给List集合,写作List<?
>,那么我们就能够这样定义上面的方法:
public void test3(List<?
>
l){ }
此时我们就能够传入以不论什么元素类型为集合形參的List了。就算是自己定义类也能够。
但另一种特殊的情形。我们不想这个List<?>是不论什么泛型List的父类,仅仅想表示它是某一类泛型List的父类。比如我们有一个圆形类(Circle)、矩形类(Rectangle),这两个类都继承了Shape抽象类,那么我们就能够这样设计:
public classTest{
public static void main(String[]
args) {
List<Circle>list = newArrayList<Circle>();
list.add(new Circle());
Canvasc = newCanvas();
c.drawAll(list);
}
}
abstract classShape{
public abstract void draw(Canvas
c);
}
class Canvas{
public String
toString() {
return "Canvas";
}
public void drawAll(List<?
extends Shape>
shapes){
for (Shape
shape : shapes){
shape.draw(this);
}
}
}
class Circle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个圆");
}
}
class Rectangle extends Shape{
public void draw(Canvas
c) {
System.out.println("在画布" +
c + "上画一个矩形");
}
}
List<? extends Shape>是受限制通配符的样例。此处的问号(?
)代表一个未知的类型。就想前面看到的通配符一样。
可是此处的这个未知类型一定是Shape的子类或Shape本身。因此我们能够把Shape称为这个通配符的上限。
这里须要注意的是,由于不知道这个受限制通配符的详细类型,所以不能把Shape对象或其子类的对象增加这个泛型集合中,比如以下的代码会报编译错误:
public void drawAll(List<?
extends Shape>
shapes){
//此处会报编译错误
shapes.add(new Rectangle());
}
Java泛型不仅同意在使用通配符形參时设定上限,并且能够在定义类型形參时设定上限,用于表示传给该类型形參的实际类型要么是该上限类型,要么是该上限类型的子类。比如:
public class Apple<T extends Number>{
T colT;
public static void main(String[]args)
{
Apple<Integer> ai = new Apple<Integer>();
Apple<Double> ad = new Apple<Double>();
//以下代码将引发编译错误,由于String类不是Number的子类型
Apple<String>
as = new Apple<String>();
}
}
3、泛型方法
所谓泛型方法,就是在声明方法时定义一个或多个类型形參。
泛型方法的使用方法格式例如以下:
修饰符<T,S> 返回值类型 方法名(形參列表){
//方法体
}
把上面方法的格式和普通方法的格式进行对照,不难发现泛型方法的方法签名比普通方法的方法签名多了类型形參声明,类型形參声明以尖括号包含起来,多个类型形參之间以逗号(,)隔开,全部的类型形參声明方法方法修饰符和方法返回值类型之间。
比如,我们要写一个这个方案,用于将一个Object数组的全部元素加入到一个Collection集合中:
static <T> void fromArrayToCollection(T[]
a,Collection<T> c){
for (T t
: a) {
c.add(t);
}
}
上面的方法是正确的,没有错误,可是在我们实际使用的过程中,我们仅仅能将数组中的元素增加Collection中。想要将一个Collection中的元素增加到还有一个Collection中,上面的方法就不适用了。
那我们可不能够这样改进上面的方法呢?
static <T> void fromArrayToCollection(Collection<T>
a,Collection<T> b){
for (T t
: a) {
b.add(t);
}
}
public static void main(String[]
args) {
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
显然,在我们调用fromArrayToCollection时会引发编译错误。这是由于编译器无法准确地判断出泛型方法中类型形參的类型。不知道应该用String还是用Object。
为了避免这样的错误,能够将方法改为这样:
static <T> void fromArrayToCollection(Collection<?
extends T>
a, Collection<T> b){
for (T t
: a) {
b.add(t);
}
}
public static void main(String[]args)
{
List<String> a = new ArrayList<String>();
List<Object> b = new ArrayList<Object>();
fromArrayToCollection(a,
b);
}
上面的方法中,将该方法前一个形參类型改为Collection<? extends T>,这样的採用类型通配符的表示方式。仅仅要该方法的前一个Collection集合里的元素类型是后一个Collection集合里的元素类型的子类就可以。
4、泛型方法和类型通配符的差别
大多数时候都能够使用泛型方法来取代类型通配符。比如对于Java的Collection接口中两个方法的定义:
public interface Collection<E>{
boolean containsAll(Collection<?
>c);
booleanaddAll(Collection<?
extends E>
c);
...
}
上面集合中的两个方法的形參都採用了类型通配符的形式,也能够採用泛型方法的形式。例如以下所看到的:
public interface Collection<E>{
<T> boolean containsAll(Collection<T>
c);
<T extends E> boolean addAll(Collection<T>
c);
...
}
上面方法使用了<Textends E>泛型形式,这时定义类型形參时设定上限(当中E是Collection接口里定义的类型形參。在该接口里E可当成普通类型使用)。
上面两个方法中类型形參T仅仅使用了一次,类型形參T产生的唯一效果是能够在不同的调用点传入不同的实际类型。
对于这样的情况。应该使用通配符:通配符就是被设计用来支持灵活的子类化的。
泛型方法同意类型形參被用来表示方法的一个或多个參数之间的类型依赖关系,或者方法返回值与參数之间的类型依赖关系。假设没有这种类型依赖关系,就不应该使用泛型方法。
Java泛型入门的更多相关文章
- 大白话说Java泛型(一):入门、原理、使用
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型(一):入门.原理.使用> 远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的 ...
- 大白话说Java泛型:入门、使用、原理
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型:入门.使用.原理> 远在 JDK 1.4 版本的时候,那时候是没有泛型的概念的.当时 Java 程序员们写集合类的代码都 ...
- java泛型 之 入门(interface)
一:泛型简单介绍: (1)所谓泛型,就是变量类型的參数化. 泛型是JDK1.5中一个最重要的特征.通过引入泛型,我们将获得编译时类型的安全和执行时更小的抛出ClassCastException的可能. ...
- 大白话说Java泛型(二):深入理解通配符
文章首发于[博客园-陈树义],点击跳转到原文<大白话说Java泛型(二):深入理解通配符> 上篇文章<大白话说Java泛型(一):入门.原理.使用>,我们讲了泛型的产生缘由以及 ...
- 《java从入门到精通》学习记录
目录 <Java从入门到精通>学习记录 3 基础的基础部分: 3 一. 常量与变量 3 1. 掌握: 3 (1) .常量与变量的声明方式: 3 (2) .变量的命名规则: 3 (3) .变 ...
- Java泛型经典文章收集
https://blog.csdn.net/s10461/article/details/53941091 Java泛型详解(从基础到入门)https://blog.csdn.net/jeffleo/ ...
- java秀发入门到优雅秃头路线导航【教学视频+博客+书籍整理】
目录 一.Java基础 二.关于JavaWeb基础 三.关于数据库 四.关于ssm框架 五.关于数据结构与算法 六.关于开发工具idea 七.关于项目管理工具Mawen.Git.SVN.Gradle. ...
- Java基础系列二:Java泛型
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.泛型概述 1.定 ...
- Java——Java泛型
该系列博文会告诉你如何从入门到进阶,一步步地学习Java基础知识,并上手进行实战,接着了解每个Java知识点背后的实现原理,更完整地了解整个Java技术体系,形成自己的知识框架. 一.泛型概述 1.定 ...
随机推荐
- git fetch, git pull 以及 FETCH_HEAD
git push. 这个很简单, 其实和后面的差不多, 这里就不讲了. 唯一需要注意的地方是: git push origin :branch2, 表示将一个内容为空的同名分支推送到远程的分支.(说白 ...
- MySQL单表恢复方法
正休息的时候一个电话将我的睡意完全打散,“开发童鞋写update SQL的时候忘了加where条件了”,相信每一个DBA同学听到这个消息的时候都有骂街的冲动吧.万幸只是单表写花了,而不是哪位大神在DB ...
- ASP.NET Core 1.0 基础之配置
来源https://docs.asp.net/en/latest/fundamentals/configuration.html ASP.NET Core 1.0支持不同的配置选项.应用配置数据可以是 ...
- Singleton 单例模式(懒汉方式和饿汉方式)
单例模式的概念: 单例模式的意思就是只有一个实例.单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例.这个类称为单例类. 关键点: 1)一个类只有一个实例 这是最基本 ...
- java生成自己定义的表ID
需生成例如以下ID: 56d7ade1-87d1-4f54-8dc8-13611c8c2545 27181ad4-4214-4e12-af3a-911a0103a12f 24cafdfb-eac3-4 ...
- OpenCV学习(7) 分水岭算法(1)
分水岭算法主要用于图像分段,通常是把一副彩色图像灰度化,然后再求梯度图,最后在梯度图的基础上进行分水岭算法,求得分段图像的边缘线. 下面左边的灰度图,可以描述为右边的地 ...
- 数学图形(1.20)N叶草
有N个叶子的草 相关软件参见:数学图形可视化工具,使用自己定义语法的脚本代码生成数学图形.该软件免费开源.QQ交流群: 367752815 vertices = t = to (*PI) r = n ...
- 采用web service传输超大数据
因为以前也没有做过相关的web service开发,对于Xfire也只是知道有这么一个框架.当然现在它已经变成apache基金会旗下的一个开源项目CXF.不过,现在依旧有很多公司还在用Xfire作we ...
- 如何设置ESXi中的虚拟机随主机一同启动?
笔者新装了几台ESXi的主机, 其中一台上面运行着一台安装了vCenter的虚拟机. 笔者一路默认, 也没改什么设置. 在试图解决其他问题的过程中, 笔者重启了ESXi. 后来发现vCente登不进 ...
- sqllite3
OS X自从10.4后把SQLite这套相当出名的数据库软件,放进了作业系统工具集里.OS X包装的是第三版的SQLite,又称SQLite3.这套软件有几个特色: 软件属于公共财(public do ...