Java面向对象系列[v1.0.0][泛型方法和类型通配符]

泛型方法和类型通配符的区别

大多数情况下,都可以使用泛型方法来代替类型通配符,对于Java的Collection接口中两个方法的定义:

public interface Collection<E>
{
	boolean containsAll(Collection<?> c);
	boolean addAll(Collection<? extends E> c);
	...
}

上述代码中的两个方法采用了类型通配符形式,也可以采用泛型方法的形式

public interface Collection<E>
{
	<T> boolean containsAll(Collection<T> c);
	<T extends E> boolean addAll(Collection<T> c);
	...
}
  • 方法中使用了泛型形式,这时定义泛型形参时设定上限(其中E是Collection接口里定义的泛型,在该接口里E可当成普通类型使用)
  • 两个方法中泛型形参T只使用了一次,泛型形参T产生的唯一效果是可以再痛的调用点传入不同的实际类型,对于这种情况应该使用通配符:它原本就是为了支持灵活的子类化而设计的
  • 泛型方法允许泛型形参被用来表示方法的一个或多个参数之间的类型依赖关系,或者方法返回值与参数之间的类型依赖关系,如果没有这样的依赖关系就不应该使用泛型方法
  • 如果某个方法中一个形参(a)的类型或返回值的类型依赖于另一个形参(b)的类型,则形参(b)的类型声明不应该使用通配符,因为形参(a)或返回值的类型依赖于该形参(b)的类型,如果形参(b)的类型无法确定,程序就无法定义形参(a)的类型,在这种情况下,只能考虑使用在方法签名中声明泛型----也就是泛型方法

同时使用泛型方法和通配符

例如Java的Collections.copy()方法

public class Collections
{
	public static <T> void copy(List<T> dest, List<? extends T> src)
	{
		...
	}
}

copy方法中的dest和src存在着明显的依赖关系,从源List中复制出来的元素,必须可以放到目标List中,所以源List集合元素的类型只能是目标集合元素的类型的子类型或者它本身
但JDK定义src形参类型时使用的是类型通配符,而不是泛型方法,因为:该方法无须向src集合中添加元素,也无须修改src集合里的元素,所以可以使用类型通配符,无须使用泛型方法,换句话说,指定上限的类型通配符支持协变,因此这种协变的集合可以安全地取出元素(协变只出不进),因此无须使用泛型方法,当然也可以将上面的方法签名改为使用泛型方法,不使用类型通配符

class Collections
{
	public static<T, S extends T> void copy(List<T> dest, List<S> src)
	{
		...
	}
}

这个方法签名可以代替前面的方法签名,但注意到泛型的形参S,它仅使用了一次,其他参数的类型、方法返回值的类型都不依赖于它,那泛型形参S就没有存在的必要,即可以使用通配符来代替S,使用通配符比使用泛型方法(在方法签名中显式声明泛型形参)更加清晰和准确,因此JDK设计该方法时采用了通配符而不是使用泛型方法
类型通配符和泛型方法(在方法签名中显式的声明泛型形参)还有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的泛型形参必须在对应方法中显示声明

发布了207 篇原创文章 · 获赞 124 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/105344446