java---泛型程序设计

- 定义简单泛型类
public class Pair<T>
{
	private T first;
	private T second;
	public Pair()
	{
		first = null;
		second = null;
	}
	
	public Pair(T first, T second)
	{
		this.first = first;
		this.second = second;
	}
	
	public T getFirst()
	{
		return first;
	}
	
	public T getSecond()
	{
		return second;
	}
	
	public void setFirst(T newValue)
	{
		first = newValue;
	}
	
	public void setSecond(T newValue)
	{
		second = newValue;
	}
}

Pair<String> nXX;
1.泛型类可以有多个类型变量
2.类型变量在整个类定义中用于指定方法的返回类型,字段,局部变量的类型。

- 泛型方法
class ArrayAlg
{
	public static <T> T getMiddle(T... a)
	{
		return a[a.length / 2];
	}
}

String middle = ArrayAlg.<String>getMiddle("John", "Q", "Public");
// 编译器可从参数推断时,<String>可以省略
String middle = ArrayAlg.getMiddle("John", "Q", "Public");
- 类型变量的限定
class ArrayAlg
{
	public static <T> T min(T[] a)
	{
		if(a == null || a.length == 0) return null;
		T smallest = a[0];
		for(int i = 1; i < a.length; i++)
		{
			if(smallest.compareTo(a[i]) > 0)
			{
				smallest = a[i];
			}
			
			return smallest;
		}
	}
}


public static <T extends Comparable> T min(T[] a) ...
T extends Comparable & Serializable
1.对泛型类型变量T设置一个限定,这样类型T必须实现接口Comparable。
2.一个类型变量或通配符可以有多个限定,多个限定使用&连接。
多个限定中至多有一个可以为类类型。
且类类型限定存在时,必须为第一个限定。
package pair2;
import java.time.*;
public class PairTest2
{
	public static void main(String[] args)
	{
		LocalDate[] birthdays = 
		{
			LocalDate.of(1906, 12, 9),
			LocalDate.of(1815, 12, 10),
			LocalDate.of(1903, 12, 3),
			LocalDate.of(1910, 6, 22),
		};
	
		Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
	}
}

class ArrayAlg
{
	public static <T extends Comparable> Pair<T> minmax(T[] a)
	{
		if(a == null || a.length == 0) return null;
		T min = a[0];
		T max = a[0];
		for(int i = 1; i < a.length; i++)
		{
			if(min.compareTo(a[i]) > 0) min = a[i];
			if(max.compareTo(a[i]) < 0) max = a[i];
		}
		
		return new Pair<>(min, max);
	}
}

泛型代码和虚拟机

虚拟机没有泛型类型对象--所有对象都属于普通类。
- 类型擦除
无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。
这个类型的名字就是去掉类型参数后的泛型类型名。
类型变量会被擦除,并替换为其限定类型。对无限定的变量则替换为Object。
// Pair<T>的原始类型如下所示
public class Pair
{
	private Object first;
	private Object second;
	public Pair(Object first, Object second)
	{
		this.first = first;
		this.second = second;
	}
	
	public Object getFirst()
	{
		return first;
	}
	
	public Object getSecond()
	{
		return second;
	}
	
	public void setFirst(Object newValue)
	{
		first = newValue;	
	}
	
	public void setSecond(Object newValue)
	{
		second = newValue;
	}
}
1.在程序中可以包含不同类型的Pair,如Pair<String>或Pair<LocalDate>。
不过擦除类型后,它们都会变成原始的Pair类型。
2.原始类型用第一个限定来替换类型变量,或如没给定限定,就替换为Object。
public class Interval<T extends Comparable & Serializable> implements Serializable
{
	private T lower;
	private T upper;
	public Interval(T first, T second)
	{
		if(first.compareTo(second) <= 0)
		{
			lower = first;
			upper = second;
		}
		else
		{
			lower = second;
			uppper = first;
		}
	}
}

// 原始类型Interval如下所示
public class Interval implements Serializable
{
	private Comparable lower;
	private Comparable upper;
	...
	public Interval(Comparable first, Comparable second)
	{
		...
	}
}
- 转换泛型表达式
编写一个泛型方法调用时,如擦除了返回类型,编译器会插入强制类型转换。
Pair<Employee> buddies = ...;
Employee buddy = buddies.getFirst();
getFirst擦除类型后的返回类型是Object。
编译器自动插入转换到Employee的强制类型转换。
也即,编译器把这个方法调用转换为两条虚拟机指令:
1.对原始方法Pair.getFirst的调用
2.将返回的Object类型强制转换为Employee类型。
当访问一个泛型字段时,也要插入强制类型转换。
// 会在结果字节码中插入强制类型转换
Employee buddy = buddies.first;
- 总结
java泛型转换,
1.虚拟机中没有泛型,只有普通的类和方法
2.所有的类型参数都会替换为它们的限定类型
3.合成的桥方法来保持多态
4.为保持类型安全性,必要时会插入强制类型转换
- 调用遗留代码

通配符类型

- 通配符类型【子类型限定】
在通配符类型中,允许类型参数发生变化。
Pair<? extends Employee>
上述通配符类型表示任何泛型Pair类型。它的类型参数是Employee的子类。
// 不能将Pair<Manager>传递给这个方法。解决方法很简单,可使用一个通配符类型
public static void printBuddies(Pair<Employee> p)
{
	Employee first = p.getFirst();
	Employee second = p.getSecond();
	System.out.println(first.getName()
		+ " and " 
		+ second.getName()
		+ " are buddies.");
// ok now
public static void printBuddies(Pair<? extends Employee> p)
{
	...
}

// new Pair(...)
var managerBuddies = new Pair<Manager>(ceo, cfo);
// Pair<? ...> xxx = new Pair(...) ok
Pair<? extends Employee> wildcardBuddies = managerBuddies;
// lowlyEmployee必须是Employee派生类
wildcardBuddies.setFirst(lowlyEmployee);
// getXXX 返回结果为Employee派生类,但存储到Employee可以
- 通配符的超类型限定
可以指定一个超类型限定
// setXXX 设置超类型,可以。设置派生类型 也可以。
// getXXX 返回超类型。要用派生类型接收结果,不可以。
// 归根到底是,可以用 基础类型接收派生类型,不可以用派生类型接收基础类型。
? super Manager


public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
	if(a.length == 0) return;
	Manager min = a[0];
	Manager max = a[0];
	for(int i = 1; i < a.length; i++)
	{
		if(min.getBonus() > a[i].getBonus()) min = a[i];
		if(max.getBonus() < a[i].getBonus()) max = a[i];
	}
	result.setFirst(min);
	result.setSecond(max);
}
这个通配符限制为Manager的所有超类型
带有超类型限定的通配符允许你写入一个泛型对象,带有子类型限定的通配符允许你读取一个泛型对象。
- 无限定通配符
// 类型Pair<?>有以下方法
? getFirst()
void setFirst(?)
getFirst的返回值只能赋给一个Object。
setFirst方法不能被调用。
public static boolean hasNulls(Pair<?> p)
{
	return p.getFirst() == null || p.getSecond() == null;
}

// 通过将hasNull转换成泛型方法,可避免使用通配符类型
public static <T> boolean hasNulls(Pair<T> p)

// 但是,带有通配符的版本可读性更好
- 通配符捕获
通配符不是类型变量,不能在编写代码中使用"?"作为一种类型。
public static <T> void swapHelper(Pair<T> p)
{
	T t = p.getFirst();
	p.setFirst(p.getSecond());
	p.setSecond(t);
}

public static void swap(Pair<?> p)
{
	// 用泛型T捕获通配符?
	swapHelper(p);
}

public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
	minmaxBonus(a, result);
	PairAlg.swapHelper(result);
}
- 通配符捕获
不能在编写代码中使用“?”作为一种类型

反射和泛型

- 泛型Class类
现在,Class类是泛型的。
如String.class实际上是一个Class<String>类的对象
- 使用Class<T>参数进行类型匹配
匹配泛型方法中Class<T>参数的类型变量有时会很有用。
public static <T> Pair<T> makePair(Class<T> c) throws InstantiationException,
	IllegalAccessException
{
	return new Pair<>(c.newInstance(), c.newInstance());
}
如果调makePair(Employee.class)
- 虚拟机中的泛型类型信息
java泛型的突出特性之一是,在虚拟机中擦除泛型特性。
擦除的类仍然保留原先泛型的微弱记忆。
如,
原始的Pair类知道它源于泛型类Pair<T>。
考虑以下方法
public static Comparable min(Comparable[] a)
// 上述是擦除一下泛型方法得到的
public static <T extends Comparable<? super T>> T min(T[] a)
可以利用反射API来确定:
这个泛型方法有一个名为T的类型参数
这个类型参数有一个子类型限定,其自身又是一个泛型类型
这个限定类型有一个通配符参数
这个通配符参数有一个超类型限定
这个泛型方法有一个泛型数组参数

为了表述泛型类型声明,可使用java.lang.reflect包中的接口Type。
这个接口包含以下子类型:
Class类,描述具体类型
TypeVariable接口,描述类型变量【如T extends Comparable<? super T>】
WildcardType接口,描述通配符【如? super T】
ParameterizedType接口,描述泛型类或接口类型【如Comparable<? super T>】
GenericArrayType接口,描述泛型数组【如T[]】
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

raindayinrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值