Effective Java 29 Consider typesafe heterogeneous containers

When a class literal is passed among methods to communicate both compile-time and runtime type information.

Map<Class<T>, Object>

   

Class's cast method

The dynamic analog of Java's cast operator. It simply checks that its argument is an instance of the type represented by the Class object. If so, it returns the argument; otherwise it throws a ClassCastException.

public class Class<T> {

T cast(Object obj);

}

Demo for a typesafe heterogeneous container.

import java.util.HashMap;

import java.util.Map;

   

/**

* @author Kaibo

*

*/

public class Favorites {

private Map<Class<?>, Object> favorites = new HashMap<Class<?>, Object>();

   

// Achieving runtime type safety with a dynamic cast

public <T> void putFavorite(Class<T> type, T instance) {

if (type == null)

throw new NullPointerException("Type is null");

favorites.put(type, type.cast(instance));

}

   

// This reestablishes this linkage between the types of key and value

public <T> T getFavorite(Class<T> type) {

return type.cast(favorites.get(type));

}

   

// Typesafe heterogeneous container pattern - client

public static void main(String[] args) {

Favorites f = new Favorites();

f.putFavorite(String.class, "Java");

f.putFavorite(Integer.class, 0xcafebabe);

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.getName());

}

}

   

Limitation

  1. A malicious client could easily corrupt the type safety of a Favorites instance, simply by using a Class object in its raw form. To deal with we need to use type.cast to the input class parameter to ensure its type safety.
  2. Favorites class cannot be used on a non-reifiable type, such as List<String>. Since List<String>.class is a syntax error. The root cause for this is that List<String> and List<Integer> are the same in the run time since they are non-reifiable at run time.

Bounded type token

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

   

asSubclass method of Class

// Use of asSubclass to safely cast to a bounded type token

static Annotation getAnnotation(AnnotatedElement 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));

}

}

   

Summary

The normal use of generics, exemplified by the collections APIs, restricts you to a fixed number of type parameters per container. You can get around this restriction by placing the type parameter on the key rather than the container. You can use Class objects as keys for such typesafe heterogeneous containers. A Class object used in this fashion is called a type token. You can also use a custom key type. For example, you could have a Database Rowtype representing a database row (the container), and a generic type Column<T> as its key.

作者:小郝
出处:http://www.cnblogs.com/haokaibo/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/haokaibo/p/consider-typesafe-heterogeneous-containers.html