栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

java---泛型程序设计

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

java---泛型程序设计

- 定义简单泛型类
public class Pair
{
	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 nXX;
- 泛型方法
class ArrayAlg
{
	public static  T getMiddle(T... a)
	{
		return a[a.length / 2];
	}
}

String middle = ArrayAlg.getMiddle("John", "Q", "Public");
// 编译器可从参数推断时,可以省略
- 类型变量的限定
class ArrayAlg
{
	public static  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;
		}
	}
}

// 对泛型类型变量T设置一个限定
public static  T min(T[] a) ...
// 一个类型变量或通配符可以有多个限定
T extends Comparable & Serializable
泛型代码和虚拟机
虚拟机没有泛型类型对象--所有对象都属于普通类。
- 类型擦除
无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。
类型变量会被擦除,并替换为其限定类型。对无限定的变量则替换为Object。
// Pair的原始类型如下所示
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;
	}
}
原始类型用第一个限定来替换类型变量,或者,如果没有给定限定,就替换为Object。
- 转换泛型表达式
编写一个泛型方法调用时,如擦除了返回类型,编译器会插入强制类型转换。
Pair buddies = ...;
Employee buddy = buddies.getFirst();
getFirst擦除类型后的返回类型是Object。
编译器自动插入转换到Employee的强制类型转换。
也即,编译器把这个方法调用转换为两条虚拟机指令:
1.对原始方法Pair.getFirst的调用
2.将返回的Object类型强制转换为Employee类型。
当访问一个泛型字段时,也要插入强制类型转换。
// 会在结果字节码中插入强制类型转换
Employee buddy = buddies.first;
- 转换泛型方法
类型擦除也会出现在泛型方法中。
public static  T min(T[] a)
是整个一组方法,而擦除类型之后,只剩下一个方法
public static Comparable min(Comparable[] a)
方法的擦除带来了两个复杂问题。
class DateInterval extends Pair
{
	public void setSecond(LocalDate second)
	{
		if(second.compareTo(getFirst()) >= 0)
		{
			super.setSecond(second);
		}
	}
}

var interval = new DateInterval(...);
Pair pair = interval;
// 若按c++的泛型,这里产生动态绑定,调用DateInterval的setSecond
// java的泛型在类型擦除实现下,基类的setSecond(Object)会由于类型匹配而不参与考虑
// 为解决此问题,编译器在DateInterval类中生成一个桥方法
// public void setSecond(Object){ setSecond((LocalDate)second);}
pair.setSecond(aDate);


// 桥方法可能会变得更奇怪
// 假设DateInterval类也覆盖了getSecond方法
class DateInterval extends Pair
{
	public LocalDate getSecond(){return (LocalDate)super.getSecond();}
}

// 在DateInterval类中,有两个getSecond方法
// LocalDate getSecond()
// Object getSecond()
在虚拟机中,会由参数类型和返回类型共同指定一个方法。
故,编译器可以为两个仅返回类型不同的方法生成字节码,虚拟机能够正确地处理这种情况。
// 一个方法覆盖另一个方法时,可以指定一个更严格的返回类型
public class Employee implements Cloneable
{
	public Employee clone() throws CloneNotSupportedException{ ... }
}
Object.clone和Employee.clone方法被称为有协变的返回类型。
实际上,Employee类有两个克隆方法
Employee clone()
Object clone()
合成的桥方法会调用新定义的方法。

java泛型转换,
1.虚拟机中没有泛型
2.所有的类型参数都会替换为它们的限定类型
3.合成的桥方法来保持多态
4.为保持类型安全性,必要时会插入强制类型转换
- 调用遗留代码
限制与局限性
- 不能用基本类型实例化类型参数
没有Pair只有Pair
- 运行时类型查询只适用于原始类型
- 不能创建参数化类型的数组
var table = new Pair[10]; // error
// 因为擦除后table为Object[]
// 数组会记住它的元素类型,如试图存储其他类型的元素,会抛出一个ArrayStoreException异常。
- Varargs警告
向参数个数可变的方法传递一个泛型类型的实例
public static  void addAll(Collection coll, T... ts)
{
	for(T t : ts)
	{
		coll.add(t);
	}
}

Collection> table = ...;
Pair pair1 = ...;
Pair pair2 = ...;
addAll(table, pair1, pair2);
为调用这个方法,java虚拟机需建立一个Pair数组,而我们已经知道不可创建泛型数组。
此时,会得到警告。
抑制警告
// 1.为包含addAll调用的方法增加注解@SuppressWarnings("unchecked")
// 2.用@SafeVarargs注解addAll方法本身
- 不能实例化类型变量
// 以下是非法的
public Pair()
{
	first = new T();
	second = new T();
}

// 可以用
Pair p = Pair.makePair(String::new);
makePair方法接收一个Supplier,这是一个函数式接口
public static  Pair makePair(Supplier constr)
{
	return new Pair<>(constr.get(), constr.get());
}
比较传统的解决方法是通过反射调用Constructor.newInstance方法来构造泛型对象。
public static  Pair makePair(Class cl)
{
	try
	{
		return new Pair<>(
			cl.getConstructor().newInstance(),
			c1.getConstructor().newInstance());	
	}
	catch(Exception e)
	{
		return null;	
	}
}

Pair p = Pair.makePair(String.class);
- 不能构造泛型数组
数组本身也带有类型,用来监控虚拟机中的数组存储,这个类型会被擦除。
public static  T[] minmax(T... a)
{
	T[] mm = new T[2];// error
}
如果数组仅仅作为一个类的私有实例字段,
则可将这个数组的元素类型声明为擦除的类型并使用强制类型转换。
String[] names = ArrayAlg.minmax(String[]::new, "Tom", "Dick", "Harry");
构造表达式String::new指示一个函数,给定所需长度,会构造一个指定长度的String数组。
public static  T[] minmax(IntFunction constr,
	T... a)
{
	T[] result = constr.apply(2);
}

// 老式
public static  T[] minmax(T... a)
{
	var result = (T[])Array.newInstance(a.getClass().getComponentType(), 2);
	...
}

// ArrayList的toArray
Object[] toArray()
T[] toArray(T[] result)
- 泛型类的静态上下文中类型变量无效
不能在静态字段或方法中引用类型变量
禁止使用带有类型变量的静态字段和方法。
- 不能抛出或捕获泛型类的实例
既不能抛出也不能捕获泛型类的对象。
泛型类扩展Throwable甚至都是不合法的。
catch子句不能使用类型变量。
- 可以取消对检查型异常的检查
java异常处理原则是,需为检查型异常提供一个处理器。
但可以利用泛型取消这个机制。
static  void throwAs(Throwable t) throws T
{
	throw (T)t;
}
假设上述方法包含在接口Task中,如有一个检查型异常e,并调用
Task.throwAs(e);
编译器会认为e是一个非检查型异常。
- 注意擦除后的冲突
泛型类型被擦除后,不允许创建引发冲突的条件。
通配符类型
- 通配符类型
在通配符类型中,允许类型参数发生变化。
Pair
上述通配符类型表示任何泛型Pair类型。它的类型参数是Employee的子类。
public static void printBuddies(Pair p)
{
	Employee first = p.getFirst();
	Employee second = p.getSecond();
	System.out.println(first.getName()
		+ " and " 
		+ second.getName()
		+ " are buddies.");
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/308726.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号