第29条:优先考虑类型安全的异构容器

介绍了一种类型安全的异构容器实现方式,该容器能够存储不同类型的数据,并通过泛型确保类型安全。通过示例代码展示了如何创建和使用这种容器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

术语 :




        有时会需要未限定固定数目的类型参数的容器,此时,可以将容器的键进行参数化而不是将容器参数化。然后将参数化的键交给容器来插入或者获得值。用泛型系统来确保值的类型和它的键相符。示例代码如下:

// Typesafe heterogeneous container pattern - implementation
public class Favorites {
	private Map<Class<?>, Object> favorites = 
			new HashMap<Class<?>, Object>();
	public <T> void putFavorite(Class<T> type, T instance) {
		if (type == null)
			throw new NullPointerException("Type is null");
		favorites.put(type, instance);
	}
	public <T> T getFavorite(Class<T> type) {
		return type.cast(favorites.get(type));
	}
}

// Demo: Typesafe heterogeneous container pattern - client
public static void main(String[] args) {
	Favorites f = new Favorites();
	f.putFavorite(String.class, "Java");
	f.putFavorite(Integer.class, 0xcafebale);
	f.putFavorite(Class.class, Favorites.class);
	String favoriteString = f.getFavorite(String.class);
	int favoriteInteger = f.getFavorite(Integer.class);
	Class<?> favoriteClass = f.getFavorite(Class.class);
	System.out.printf("%s %x %s%n", favoriteString, favoriteInteger,
			                        favoriteClass);
}
        上面的代码中,class方法返回的是一个class<T>的形式。Favorites是类型安全的,它总是按照键返回正确的值,同时它也是异构的,因为它的容器的键不是同一种类型,这有别于传统的Map,因此,将Favorites称作类型安全的异构容器。异构来自哪?答案是无限制通配符的键Class<?>,在这里它仅代表是某种class,因此允许将不同类的class放入同一个Map,这就是异构的原因。注意,因为我们使用的值类型是Object,因此Map并无法保证键一对能对应正确的值,它只知道值是一个Object就可以了,这种对应的关系是实现者自己来确保的。手动重新建立类型与值之间的关系是在getFavorite方法中进行的,利用Class的cast方法,将对象动态地转换成Class对象所表示的类型。cast只是检验它的参数是不是为Class对象所表示的类型的实例。如果是,就返回参数,如果不是就抛出ClassCastException。

public class Class<T> {
	T cast(Object obj);
}
        Favorites类有两种局限:一是恶意用户可以通过使用原生态形式的Class来破坏年Favorites实例的类型安全。这种方式可以通知在putFavorite中进行类型检查来确保实例对象进行检查。

	public <T> void putFavorite(Class<T> type, T instance) {
		if (type == null)
			throw new NullPointerException("Type is null");
		favorites.put(type, type.cast(instance);
	}
        第二个局限性在于它不能用在不可具体化的类型中。比如说可以存储喜爱的String,String[],但是不能存储List<String>。因为 List<String>.class是语法错误。因为在运行时他们的类型会被擦除,所在List<String>与List<Integer>实际上是共用一个Class。如果需要限制些可以传递给方法的类型,则可以使用有限制的通配符类型。
       
public <T extends Annotation>
      T getAnnotation(Class<T> annotationType);
        在这面这段代码里,如果想把一个Class<?>传递给getAnnotation方法,那么按照要求,可能想到可以将其转换成Class<? extends Annotation>,但是这种行为是非受检的,会收到编译器的警告,但是,可以利用Class类提供的一个安全且动态地执行这种转换的实例方法,asSubclass,它将调用它的Class对象转换成用其参数表示的类的一个子类型,如果转换成功,该方法就返回它的参数,如果失败则抛出ClassCastException。见如下例子:

public <T extends Annotation>
      T getAnnotation(Class<T> annotationType);

// Use of asSubclass to safely cast to a bounded type token
static Annotation getAnnocation(AnnocationElement element,
		                        String annotationTypeName) {
	Class<?> annotationType = null;// Unbounded type token
	try {
		annotationType = Class.forName(annotationTypeName);
	} catch (Exception ex) {
		throw new IllegalArgumentException(ex);
	}
	return element.getAnnotation {
		annotationType.asSubclass(Annotation.class);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值