Java泛型入门



 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产生的唯一效果是能够在不同的调用点传入不同的实际类型。

对于这样的情况。应该使用通配符:通配符就是被设计用来支持灵活的子类化的。

泛型方法同意类型形參被用来表示方法的一个或多个參数之间的类型依赖关系,或者方法返回值与參数之间的类型依赖关系。假设没有这种类型依赖关系,就不应该使用泛型方法。

时间: 2024-10-12 22:55:56

Java泛型入门的相关文章

面向对象,Java泛型篇

一.Java泛型入门基础 1. 泛型历史:集合中可以存储任意类型对象,但是在取出时,如果要使用具体对象的特有方法时,需要进行向下转型,如果存储的对象类型不一致,在转型过程中就会出现ClassCastException异常.这样就给程序带来了不安全性. 在jdk1.5以后就有了解决方案--泛型技术:在存储元素时,就不允许存储不同类型的元素.存储了就编译失败. 所以就需要在存储元素时,在容器上明确具体的元素类型,这其实和数组定义很像. 2.优势:1)将运行时期的ClassCastException异

java泛型 之 入门(interface)

一:泛型简介: (1)所谓泛型,就是变量类型的参数化. 泛型是JDK1.5中一个最重要的特征.通过引入泛型,我们将获得编译时类型的安全和运行时更小的抛出ClassCastException的可能.在JDK1.5中,你可以声明一个集合将接收/返回的对象的类型.使用泛型时如果不指明参数类型,即泛型类没有参数化,会提示警告,此时类型为Object. (2)为什么使用泛型 使用泛型的典型例子,是在集合中的泛型使用.如果不使用泛型,存入集合中的元素可以是任何类型的,当从集合中取出时,所有的元素都是Obje

Java泛型中的PECS原则

今天在写代码的时候使用到了这样一个方法签名: public void foo(Map<String, String> map); 在写这个参数的时候正好在想一些关于泛型的东西,于是: public void foo(Map<? extends String, ? extends String> map); 这两种写法有什么区别呢?记得以前和同学讨论过这个问题,但后来没有记下来,渐渐又淡忘了.今天又去翻了好多资料,总算找到一些可以参考的,赶紧记在这里方便以后温故知新啦.好了,言归正传

Java泛型的协变

在上篇<Java泛型的基本使用>这篇文章中遗留下面问题,即将子类型也能加入到父类型的泛型中.要实现这样的功能必须借助于协变. 实验准备 如今在上篇文章展示的Decorator类型的基础上,添加一些代码,如代码清单1所看到的. 代码清单1 /** * * 描 述:Exp2使用br/> * 作 者:jiaan.gja<br/> * 历 史: (版本号) 作者 时间 凝视 <br/> * @param itemList */ public void doDecorate

2017.4.5 Java 泛型

Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型. 泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数. 假定我们有这样一个需求:写一个排序方法,能够对整形数组.字符串数组甚至其他任何类型的数组进行排序,该如何实现? 答案是可以使用 Java 泛型. 使用 Java 泛型的概念,我们可以写一个泛型方法来对一个对象数组排序.然后,调用该泛型方法来对整型数组.浮点数数组.字符串数组等进行排

关于Java泛型的使用

在目前我遇到的java项目中,泛型应用的最多的就属集合了.当要从数据库取出多个对象或者说是多条记录时,往往都要使用集合,那么为什么这么使用,或者使用时有什么要注意的地方,请关注以下内容. 感谢Windstep. 原文链接:http://www.cnblogs.com/lwbqqyumidi/p/3837629.html 原文标题:Java总结篇系列:Java泛型 (我的第一篇水文,233)

java 泛型 窜讲

一.为什么使用泛型      复用性:泛型的本质就是参数化类型,因而使用编写的泛型代码可以被许多不同类型的对象所复用.      安全性:在对类型Object引用的参数操作时,往往需要进行显式的强制类型转换.这种强制类型转换需要在运行时才能被发现是否转换异常,通过引入泛型能将在运行时才能检查类型转换,提前到编译时期就能检查. 二.自定义泛型 java中自定义泛型分为三种:泛型类.泛型接口.泛型方法. 下面使用一个案例演示泛型类.泛型方法,泛型接口类似,所以不再演示. // 自定义泛型类publi

1月21日 - (转)Java 泛型

java泛型 什么是泛型? 泛型(Generic type 或者 generics)是对 Java 语言的类型系统的一种扩展,以支持创建可以按类型进行参数化的类.可以把类型参数看作是使用参数化类型时指定的类型的一个占位符,就像方法的形式参数是运行时传递的值的占位符一样. 可以在集合框架(Collection framework)中看到泛型的动机.例如,Map 类允许您向一个 Map 添加任意类的对象,即使最常见的情况是在给定映射(map)中保存某个特定类型(比如 String)的对象. 因为 M

Java泛型_上界extends_下界super

?Java泛型_上界extends_下界super ? 通配符类型 <? extends T> 表示类型的上界,表示参数化类型的可能是T或是T的子类 <? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型(T)的超类型(父类型),直至Object 当使用 Upper Bound 通配符时 如下代码, /**  * 代码中通配符<?> 是 <? extends Object> 的简写  *  * @param list