《Java编程思想》学习笔记8——泛型编程高级

原创 2011年09月22日 17:44:56

1.泛型边界:

Java泛型编程时,编译器忽略泛型参数的具体类型,认为使用泛型的类、方法对Object都适用,这在泛型编程中称为类型信息檫除。

例如:

class GenericType{
	public static void main(String[] args){
		System.out.println(new ArrayList<String>().getClass());
		System.out.println(new ArrayList<Integer>().getClass());
}
}

输出结果为:

java.util.ArrayList

java.util.ArrayList

泛型忽略了集合容器中具体的类型,这就是类型檫除。

但是如果某些泛型的类/方法只想针对某种特定类型获取相关子类应用,这时就必须使用泛型边界来为泛型参数指定限制条件。

例如:

interface HasColor{
	java.awt.Color getColor();
}
class Colored<T extends HasColor>{
	T item;
	Colored(T item){
		this.item = item;
}
java.awt.Color color(){
	//调用HasColor接口实现类的getColor()方法
	return item.getColor();
}
}
class Dimension{
	public int x, y, z;
}
Class ColoredDimension<T extends Dimension & HasColor>{
	T item;
	ColoredDimension(T item){
		this.item = item;
}
T getItem(){
	return item;
}
java.awt.Color color(){
	//调用HasColor实现类中的getColor()方法
	return item.getColor();
}
//获取Dimension类中定义的x,y,z成员变量
int getX(){
	return item.x;
}
int getY(){
	return item.y;
}
int getZ(){
	return item.z;
}
}
interface Weight{
	int weight();
}
class Solid<T extends Dimension & HasColor & Weight>{
	T item;
	Solide(T item){
		this.item = item;
}
T getItem(){
	return item;
}
java.awt.Color color(){
	//调用HasColor实现类中的getColor()方法
	return item.getColor();
}
//获取Dimension类中定义的x,y,z成员变量
int getX(){
	return item.x;
}
int getY(){
	return item.y;
}
int getZ(){
	return item.z;
}
int weight(){
	//调用Weight接口实现类的weight()方法
	return item.weight();
}
}
class Bounded extends Dimension implements HasColor, Weight{
	public java.awt.Color getColor{
		return null;
}
public int weight(){
	return 0;
}
}
public class BasicBounds{
	public static void main(String[] args){
		Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
		solid.color();
		solid.getX();
		solid.getY();
		solid.getZ();
		solid.weight();
}
}

Java泛型编程中使用extends关键字指定泛型参数类型的上边界(后面还会讲到使用super关键字指定泛型的下边界),即泛型只能适用于extends关键字后面类或接口的子类。

Java泛型编程的边界可以是多个,使用如<T extends A & B & C>语法来声明,其中只能有一个是类,并且只能是extends后面的第一个为类,其他的均只能为接口(和类/接口中的extends意义不同)。

使用了泛型边界之后,泛型对象就可以使用边界对象中公共的成员变量和方法。

2.泛型通配符:

泛型初始化过程中,一旦给定了参数类型之后,参数类型就会被限制,无法随着复制的类型而动态改变,如:

class Fruit{
}
class Apple extends Fruit{
}
class Jonathan extends Apple{
}
class Orange extends Fruit{
}
如果使用数组:
public class ConvariantArrays{
	Fruit fruit = new Apple[10];
	Fruit[0] = new Apple();
	Fruit[1] = new Jonathan();
	try{
		fruit[0] = new Fruit();
}catch(Exception e){
	System.out.println(e);
}
try{
		fruit[0] = new Orange();
}catch(Exception e){
	System.out.println(e);
}
}

编译时没有任何错误,运行时会报如下异常:

java.lang.ArrayStoreException:Fruit

java.lang.ArrayStoreException:Orange

为了使得泛型在编译时就可以进行参数类型检查,我们推荐使用java的集合容器类,如下:

public class NonConvariantGenerics{
	List<Fruit> flist = new ArrayList<Apple>();
}

很不幸的是,这段代码会报编译错误:incompatible types,不兼容的参数类型,集合认为虽然Apple继承自Fruit,但是List的Fruit和List的Apple是不相同的,因为泛型参数在声明时给定之后就被限制了,无法随着具体的初始化实例而动态改变,为解决这个问题,泛型引入了通配符”?”。

对于这个问题的解决,使用通配符如下:

public class NonConvariantGenerics{
	List<? extends Fruit> flist = new ArrayList<Apple>();
}

泛型通配符”?”的意思是任何特定继承Fruit的类,java编译器在编译时会根据具体的类型实例化。

另外,一个比较经典泛型通配符的例子如下:

public class SampleClass < T extendsS> {…}

假如A,B,C,…Z这26个class都实现了S接口。我们使用时需要使用到这26个class类型的泛型参数。那实例化的时候怎么办呢?依次写下

SampleClass<A> a = new SampleClass();

SampleClass<B> a = new SampleClass();

SampleClass<Z> a = new SampleClass();

这显然很冗余,还不如使用Object而不使用泛型,使用通配符非常方便:

SampleClass<? Extends S> sc = newSampleClass();

3.泛型下边界:

在1中大概了解了泛型上边界,使用extends关键字指定泛型实例化参数只能是指定类的子类,在泛型中还可以指定参数的下边界,是一super关键字可以指定泛型实例化时的参数只能是指定类的父类。

例如:

class Fruit{
}
class Apple extends Fruit{
}
class Jonathan extends Apple{
}
class Orange extends Fruit{
}
public superTypeWildcards{
	public static void writeTo(List<? super Apple> apples){
		apples.add(new Apple());
		apples.add(new Jonathan());
}
}

通过? Super限制了List元素只能是Apple的父类。

泛型下边界还可以使用<?super T>,但是注意不能使用<Tsuper A>,即super之前的只能是泛型通配符,如:

public class GenericWriting{
	static List<Apple> apples = new ArrayList<Apple>();
	static List<Fruit> fruits = new ArrayList<Fruit>();
	static <T> void writeExact(List<T> list, T item){
		list.add(item);
}
static <T> void writeWithWildcards(List<? super T> list, T item){
	list.add(item);
}
static void f1(){
	writeExact(apples, new Apple());
}
static void f2(){
writeWithWildcards(apples, new Apple());
	writeWithWildcards(fruits, new Apple());
}
public static void main(String[] args){
	f1();
	f2();
}
}

4.无边界的通配符:

泛型的通配符也可以不指定边界,没有边界的通配符意思是不确定参数的类型,编译时泛型檫除类型信息,认为是Object类型。如:

public class UnboundedWildcard{
	static List list1;
	static List<?> list2;
	static List<? extends Object> list3;
	static void assign1(List list){
		list1 = list;
		list2 = list;
		//list3 = list; //有未检查转换警告
} 
static void assign2(List<?> list){
		list1 = list;
		list2 = list;
	list3 = list;
}
static void assign3(List<? extends Object> list){
		list1 = list;
		list2 = list;
	list3 = list;
}
public static void main(String[] args){
	assign1(new ArrayList());
assign2(new ArrayList());
//assign3(new ArrayList()); //有未检查转换警告
assign1(new ArrayList<String>());
assign2(new ArrayList<String>());
assign3(new ArrayList<String>()); 
List<?> wildList = new ArrayList();
assign1(wildList);
assign2(wildList);
assign3(wildList); 
}
}

List和List<?>的区别是:List是一个原始类型的List,它可以存放任何Object类型的对象,不需要编译时类型检查。List<?>等价于List<Object>,它不是一个原始类型的List,它存放一些特定类型,只是暂时还不确定是什么类型,需要编译时类型检查。因此List的效率要比List<?>高。

5.实现泛型接口注意事项:

由于泛型在编译过程中檫除了参数类型信息,所以一个类不能实现以泛型参数区别的多个接口,如:

interface Payable<T>{
}
class Employee implements Payable<Employee>{
}
class Hourly extends Employee implements Payable<Hourly>{
}

类Hourly无法编译,因为由于泛型类型檫除,Payable<Employee>和Payable<Hourly>在编译时是同一个类型Payable,因此无法同时实现一个接口两次。

6.泛型方法重载注意事项:

由于泛型在编译时将参数类型檫除,因此以参数类型来进行方法重载在泛型中要特别注意,如:

public class GenericMethod<W,T>{
	void f(List<T> v) {
}
void f(List<W> v){
}
}

无法通过编译,因为泛型檫除类型信息,上面两个方法的参数都被看作为Object类型,使用参数类型已经无法区别上面两个方法,因此无法重载。

7.泛型中的自绑定:

通常情况下,一个类无法直接继承一个泛型参数,但是你可以通过继承一个声明泛型参数的类,这就是java泛型编程中的自绑定,如:

class SelfBounded<T extends SelfBounded<T>>{
	T element;
	SelfBounded<T> set(T arg){
		Element = arg;
		return this;
} 
T get(){
	return element;
}
}
class A extends SelfBounded<A>{
}
class B extends SelfBounded<A>{
}
class C extends SelfBounded<C>{
	C setAndGet(C arg){
		set(arg);
		return get();
}
}
public class SelfBounding{
	public static void main(String[] args){
		A a = new A();
		a.set(new A());
		a = a.set(new A()).get();
		a = a.get();
		C c = new C();
		C = c.setAndGet(new C());
}
}

泛型的自绑定约束目的是用于强制继承关系,即使用泛型参数的类的基类是相同的,强制所有人使用相同的方式使用参数基类。


java编程思想读书笔记 第十五章 泛型 (泛型方法)

泛型方法 首先需要知道的是,可以在类中包含参数化方法,而这个方法所在的类可以是泛型类,也可以不是泛型类。也就是说,是否拥有泛型方法,与其所在的类是否是泛型没有关系。泛型方法使得该方法能够独立于类而产...
  • abc709272013
  • abc709272013
  • 2016-10-21 22:35:45
  • 459

Java编程思想第四版 第15章 泛型

第15章 泛型 1 与C的比较 2 简单泛型 21 一个元组类库 22 一个堆栈类 23 RandomList 3 泛型接口 4 泛型方法 41杠杆利用类型参数推断 42 可变参数与泛型方法 43 用...
  • bxh7425014
  • bxh7425014
  • 2017-01-21 15:27:59
  • 1272

Java编程思想之泛型(拓展)

问题在使用Java泛型时会出现的各类问题1) 任何基本类型都不能作为类型参数即ArrayList< int >之类的是不行的,不过使用用基本类型的包装器类以及Java SE5的自动包装机制就能解决。列...
  • ai_xao
  • ai_xao
  • 2017-09-05 13:55:31
  • 304

java编程思想读书笔记 第十五章 泛型(自限定的类型)

1. 自限定的类型 在java泛型中,经常会出现如下的泛型写法:class SelfBounded SelfBounded类接受泛型参数T,而T由一个边界类限定,这个边界就是拥有T作为其参数的Sel...
  • abc709272013
  • abc709272013
  • 2016-10-24 22:51:05
  • 994

Java编程思想之泛型

1.     泛型实现了参数化类型的概念,意思是适用于许多许多的类型。在创建参数化类型的一个实例时,编译器会负责转型操作,并保证类型的正确性。 2.     泛型的主要目的之一是用来指定容器要持有什...
  • qq_33403693
  • qq_33403693
  • 2017-08-16 13:37:56
  • 126

Java编程思想读书笔记——泛型(二)

15.7 擦除的神秘之处 泛型的类型与类型参数无关。 package com.mzm.chapter15; import java.util.ArrayList; /** * 泛型的类型...
  • baidu_21088863
  • baidu_21088863
  • 2018-01-28 22:10:43
  • 42

Java泛型编程最全总结

1介绍 Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。下面是一个不用泛型例子:   Java代码   List myIntLis...
  • u014386474
  • u014386474
  • 2016-07-29 16:23:22
  • 709

夯实JAVA基本之一——泛型详解(2):高级进阶

前言:被温水煮惯了,梦想的东西总是不敢于尝试,失败了又怎样,最多从头来过。上一篇给大家初步讲解了泛型变量的各种应用环境,这篇将更深入的讲解一下有关类型绑定,通配符方面的知识。一、类型绑定1、引入我们重...
  • harvic880925
  • harvic880925
  • 2015-11-17 10:24:30
  • 10253

java泛型程序设计——定义简单泛型类+泛型方法

【0】README0.1) 本文描述+源代码均 转自 core java volume 1, 旨在理解 java泛型程序设计 的 定义泛型类+泛型方法的知识;【1】一个泛型类: 就是具有一个或多个类型...
  • PacosonSWJTU
  • PacosonSWJTU
  • 2015-12-07 13:58:40
  • 1381

泛型--Java使用泛型编程提取类名上T类型

转载自:http://blog.csdn.net/ykdsg/article/details/5472591   很早之前写过利用泛型和反射机制抽象DAO ,对其中获取子类泛型的clas...
  • szzt_lingpeng
  • szzt_lingpeng
  • 2016-12-28 23:15:40
  • 6675
收藏助手
不良信息举报
您举报文章:《Java编程思想》学习笔记8——泛型编程高级
举报原因:
原因补充:

(最多只允许输入30个字)