{Effective Java} Chap 5 Generics


      

      With generics, you tell the compiler what types of objects are permitted in each collection. The compiler inserts casts for you automatically and tells you at compile time if you try to insert an object of the wrong type. This results in programs that are both safer and clearer. This chapter tells you how to maximize the benefits and minimize the complications. 


Item 23: don't use raw types in new code, use generics

From JDK 1.5, we can use generics. 

Designers still allow us to use raw types such as Collection stamps = ... becasue of migration compatibility.

But we should use Collections<Stamp> to declare fields.

If you use raw types like List, you lose type safety. But not if you use a parameterized type like List<Object>. 

Notice: List<String> is not a subtype of List<Object> but a subtype of List.


Unbounded wildcard types are also safe and flexible, such as Set<?> which is capable of holding any set.

Notice: But you can't put any element (other than null) into a Set<?>, and can't assume anything about the type of the objects that you get out from this set.


You must use raw types in class literals, such as String[].class, List.class. But not List<String>.class!

You cannot use instanceof on List<String>, but you can on List<?>.

//Legitimate use of raw type - instanceof operator
if(o instanceof Set) 	// Raw type
{
	Set<?> m = (Set<?>) o; //Wildcard type
}


In summary, Set<Object> and Set<?> are safe but Set is not safe. We'd better use parameterized type.


Item 24: eliminate unchecked warnings

Eliminate all unchecked warning that you can.

If you can't eliminate a warning, you canprove that the code that provoked the warning is typesafe,then and only then suppress the warning with an @SuppressWarning("unchecked") annotation.

Always use the SuppressWarnings annotation on thesmallest scope possible. 

Such as a very short method or variable declaration. Never use it over the whole class.

When the return value should be annotated, we can createa local variable to hold the return value and annotete its declaration.

//Adding local variable to reduce scope of @SuppressWarning
public <T> T[] toArray(T[] a)
{
	if(a.length < size)
	{
		//This cast is correct because the array we're creating 
		//is of the same type as the one passed in, which is T[]
		@SuppressWarnings("Unchecked")
		T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
		return result;
	}
	
	System.arraycopy(elements, 0, a, 0, size);
	if(a.length > size)
		a[size] = null;
	return a;
}

Every time you use an @SuppressWarnings("unchecked") annotation, add a comment saying why it's safe to do so.


In summary, every unchecked warning represents the potential for a ClassCastException at runtime. Do your best to eliminate these warnings.


Item 25: prefer lists to arrays

Arrays are covariant(协变), which means if Sub is a subtype of Super, then Sub[] is a subtype of Super[].

Generics are invariant and more effecient than arrays.

Differences:

1. we can find error at compile time for generics.

2. arrays are reified, which means they know and enforce their types at runtime. E.g. you can't store a String into an array of Long.

   generics are implemented by erasure, which means they enforce types at complie time and dicard/erase their type information at runtime.

   the only parameterized types are reifiable are unbounded wildcard types such as List<?>.


So you can't mix them. Because it's not typesafe. 

Notice: Java doesn't support lists natively, some generic types such as ArrayList must be implemented atop arrays. Other generic types such as HashMap are implemented atop arrays for performance.


In summary, we'd better use List<E> rather than E[]. Arrays are covariant and refied and also can provide runtime type safety but not complie-time safety.  


Item 26: favor generic types

Writing your own generic types is a bit more difficult but it's worth the effort to learn how.

This is an example about changing Object-based stack to a generic stack.

	
	//Object-based collection - not good
	public class Stack {
		private Object[] elements;
		private int size = 0;
		private static final int DEFAULT_INITIAL_CAPACITY = 16;

		public Stack() {
			elements = new Object[DEFAULT_INITIAL_CAPACITY];
		}

		public void push(Object e) {
			ensureCapacity();
			elements[size++] = e;
		}

		public Object pop() {
			if (size == 0)
				throw new EmptyStackException();
			Object result = elements[--size];
			elements[size] = null;
			return result;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		private void ensureCapacity() {
			if (elements.length == size)
				elements = Arrays.copyOf(elements, 2 * size + 1);
		}
	}
	
	//Initial attempt to generify Stack
	public class Stack<E> {
		private E[] elements;
		private int size = 0;
		private static final int DEFAULT_INITIAL_CAPACITY = 16;

		public Stack() {
			//Notice: we cannot mix array and generic types 
			//elements = new E[DEFAULT_INITIAL_CAPACITY];
			
			//1st solution: just one cast 
			//The elements array will contain only E instances from push(E)
			//This is sufficient to ensure type safety, but the runtime
			//type of the array won't be E[]; it will always be Object[]!
			@SuppressWarnings("unchecked")
			elements = (E[])new Object[DEFAULT_INITIAL_CAPACITY];
			
			//2nd solution: change elements' type to Object, but then we need to do this: (E) elements[-- size], which need many casts
			
		}

		public void push(E e) {
			ensureCapacity();
			elements[size++] = e;
		}

		public E pop() {
			if (size == 0)
				throw new EmptyStackException();
			E result = elements[--size];
			elements[size] = null;
			return result;
		}

		public boolean isEmpty() {
			return size == 0;
		}

		private void ensureCapacity() {
			if (elements.length == size)
				elements = Arrays.copyOf(elements, 2 * size + 1);
		}
	} 
	
	
	//Stack<String> stack = new Stack<String>();



We cannot create generic primitive types such as List<double>. Why???

We can also create generic types that restrict the permissible values of their type parameter such as <E extends List>.


In summary , generic types are safer and easier to use than types that require casts in client code. Generify your existing types as time permits. This will make life easier for new users of these types without breaking existing clients.


Item 27: favor generic methods

The type parameter list, which declares the type parameter, goes between the method's modifiers and its return type.

E.g. <E> is the type parameter list and Set<E> is the return type.

public static E Set<E> union(Set<E> S1, Set<E> S2) {
		Set<E> result = new HashSet<E>(S1);
		result.addAll(S2);
		return result;
		
	}

Type inference:

generic methods can infer the arguement type. So we don't need to specify the type implicitly.

Just as item 1, we can create generic static factory method to avoid duplication.

Generic Singleton Factory:


In summary, generic methods like generic types are easier and safer.


Item 28: use bounded wildcards to increase API flexibility

Bounded wildcard type:

Iterable<? extends E>

Collection<? super E>


For maximum flexibility, use wildcard types on input parameters and represent producers or consumers.

PECS(Get and Put principle): producer-extends, consumer-super.

Do not use wildcard types as return types, because it would force users to use wildcard types in client code.

If the user of a class has to think about wildcard types, there is probably something worry with the class's API.

If the compiler doesn't infer the type that you wish to had, you can tell it what type to use with an explicit type parameter.

Always use Comparable<? super T> in preference to use Comparable<T>.

If a type parameter appears only once in a method declaration, replace it with a wildcard.


In summary, if you write a library that will be widely used, the proper use of wildcard types should be considered mandatory. Remember that all comparables and comparators are consumers.


Item 29: consider typesafe heterogeneous containers

The most common use of generics is for collections, such as Set and Map, and single-element containers such as ThreadLocal and AtomicReference. We have a limited number of type arguments.

When we want many parameters, we can use parameterized key.

Type Token: String.class is type of Class<String>.


//Typesafe heterogeneous container pattern API
public class Favorites{
	private Map<Class<?>, Object> favoritesMap = new HashMap<Class<?>, Object>();
	public <T> void putFavorite(Class<T> type, T instance)
	{
		if(type == null)
			throw new NullPointerException();
		favoritesMap.put(type, instance);
	}
	
	public <T> T getInstance(Class<T> type)
	{
		return type.cast(favoritesMap.get(type));
	}
}



In summary, you can use Class objects as keys for such typesafe heterogeneous containers. And you can also use a custom key type such as DatabaseRow type and a generic type Column<T> as a database row.





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Anyanyamy

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

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

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

打赏作者

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

抵扣说明:

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

余额充值