【Java基础】泛型

Num1:请不要在新代码中使用原生类型

泛型类和接口统称为泛型。每种泛型定义一组参数化的类型,构成格式是:类或接口名称,接着用<>把对应于泛型形式类型的参数的实际参数列表括起来。比如:List是一个参数化的类型,表示元素类型为String的列表。最后一点,每个泛型都定义一个原生类型,raw type,即不带任何实际类型参数的泛型名称。

示例代码:

public class Raw {
	// Uses raw type (List) - fails at runtime! - Page 112
	public static void main(String[] args) {
		List<String> strings = new ArrayList<String>();
		unsafeAdd(strings, new Integer(42));
		String s = strings.get(0); // Compiler-generated cast
	}

	private static void unsafeAdd(List list, Object o) {
		list.add(o);
	}

	// Use of raw type for unknown element type - don't do this! - Page 113
	static int rawNumElementsInCommon(Set s1, Set s2) {
		int result = 0;
		for (Object o1 : s1)
			if (s2.contains(o1))
				result++;
		return result;
	}

	// Unbounded wildcard type - typesafe and flexible - Page 113
	static int numElementsInCommon(Set<?> s1, Set<?> s2) {
		int result = 0;
		for (Object o1 : s1)
			if (s2.contains(o1))
				result++;
		return result;
	}
}

从Java1.5发行版本开始,Java就提供了一种安全的替代方法,称作无限制的通配符类型,如果使用泛型,但不确定或者不关心实际的类型参数,就可以使用一个问号代替。

那么无限制通配类型Set<?>和原生类型Set之间有什么区别呢?通配符类型是安全的,原生类型则不安全。

Num2:消除非受检警告

当使用泛型编程时,会遇到许多编译器警告,那么该如何消除?

可以用@SuppressWarnings("unchecked")这个注解来禁止警告。需要注意的是,每当使用@SuppressWarnings("unchecked")注解时,都要添加一条注释,说明为什么这么做是安全的,这样可以帮助其他人理解代码,更重要的是,可以尽量减少其他人修改代码后导致计算不安全的概率。

Num3:列表优先于数组

数组与泛型相比,有两个重要的不同点。

首先,数组是协变的,泛型则是不可变的。

第二大区别:数组是具体化的,因此数组会在运行时才知道并检查它们的元素类型约束。相比之下,泛型则是通过擦除来实现的,因此泛型只在编译时强化它们的类型信息,并在运行时丢弃它们的元素类型信息。

示例代码:

public class Reduction {
	static <E> E reduce(List<E> list, Function<E> f, E initVal) {
		List<E> snapshot;
		synchronized (list) {
			snapshot = new ArrayList<E>(list);
		}
		E result = initVal;
		for (E e : snapshot)
			result = f.apply(result, e);
		return result;
	}

	// A few sample functions
	private static final Function<Integer> SUM = new Function<Integer>() {
		public Integer apply(Integer i1, Integer i2) {
			return i1 + i2;
		}
	};

	private static final Function<Integer> PRODUCT = new Function<Integer>() {
		public Integer apply(Integer i1, Integer i2) {
			return i1 * i2;
		}
	};

	private static final Function<Integer> MAX = new Function<Integer>() {
		public Integer apply(Integer i1, Integer i2) {
			return Math.max(i1, i2);
		}
	};

	private static final Function<Integer> MIN = new Function<Integer>() {
		public Integer apply(Integer i1, Integer i2) {
			return Math.min(i1, i2);
		}
	};

	public static void main(String[] args) {
		List<Integer> intList = Arrays.asList(2, 7, 1, 8, 2, 8, 1, 8, 2, 8);

		// Reduce intList using each of the above reducers
		System.out.println(reduce(intList, SUM, 0));
		System.out.println(reduce(intList, PRODUCT, 1));
		System.out.println(reduce(intList, MAX, Integer.MIN_VALUE));
		System.out.println(reduce(intList, MIN, Integer.MAX_VALUE));
	}
}

Num4:优先考虑泛型类和方法

示例类代码:

public class Stack<E> {
	private E[] elements;
	private int size = 0;
	private static final int DEFAULT_INITIAL_CAPACITY = 16;

	// 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")
	public Stack() {
		elements = (E[]) new Object[DEFAULT_INITIAL_CAPACITY];
	}

	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; // Eliminate obsolete reference
		return result;
	}

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

	private void ensureCapacity() {
		if (elements.length == size)
			elements = Arrays.copyOf(elements, 2 * size + 1);
	}

	// Little program to exercise our generic Stack
	public static void main(String[] args) {
		Stack<String> stack = new Stack<String>();
		for (String arg : args)
			stack.push(arg);
		while (!stack.isEmpty())
			System.out.println(stack.pop().toUpperCase());
	}
}

如果类可以从泛型中收益一般,方法也一样,静态工具方法尤其适合于泛型化。

示例方法代码:单例工厂模式

public interface UnaryFunction<T> {
	T apply(T arg);
}

public class GenericSingletonFactory {
	// Generic singleton factory pattern
	private static UnaryFunction<Object> IDENTITY_FUNCTION = new UnaryFunction<Object>() {
		public Object apply(Object arg) {
			return arg;
		}
	};

	// IDENTITY_FUNCTION is stateless and its type parameter is
	// unbounded so it's safe to share one instance across all types.
	@SuppressWarnings("unchecked")
	public static <T> UnaryFunction<T> identityFunction() {
		return (UnaryFunction<T>) IDENTITY_FUNCTION;
	}

	// Sample program to exercise generic singleton
	public static void main(String[] args) {
		String[] strings = { "jute", "hemp", "nylon" };
		UnaryFunction<String> sameString = identityFunction();
		for (String s : strings)
			System.out.println(sameString.apply(s));

		Number[] numbers = { 1, 2.0, 3L };
		UnaryFunction<Number> sameNumber = identityFunction();
		for (Number n : numbers)
			System.out.println(sameNumber.apply(n));
	}
}

示例方法代码:静态方法模式

public class GenericStaticFactory {
	// Generic static factory method
	public static <K, V> HashMap<K, V> newHashMap() {
		return new HashMap<K, V>();
	}

	public static void main(String[] args) {
		// Parameterized type instance creation with static factory
		Map<String, List<String>> anagrams = newHashMap();
	}
}

泛型方法一个显著特征:无需明确指定类型参数的值,不像调用泛型构造器的时候必须指定一个类型。

简而言之,使用泛型比使用需要在客户端代码中进行转换的类型来的更加安全,也更加容易。

posted @ 2016-06-27 15:41  cryAllen  阅读(259)  评论(0编辑  收藏  举报