[自用]Java——泛型方法

泛型方法

在一些情况下,定义类、接口时没有使用到泛型形参,但一些方法却又需要自定义泛型形参时可以使用泛型方法
Java5开始提供对泛型方法的支持

定义泛型方法

泛型方法的语法格式如下:

修饰符 < A , B ,... >返回值类型 方法名(形参列表)
{
	//方法逻辑代码
}

举例:
创建一个将数组的内容添加到集合的静态方法:

static  <T>  void ArrayToCollection( T[] a , Collection <T> c)
{
	for (T o : a)
	{
		c.add(o);
	}
}

上面代码片段的泛型方法中定义了一个T泛型形参,这个T类型可以在该方法中当成普通变量来使用注意
与接口、类声明中定义的泛型的不同之处:

  1. 作用域的不同。方法中定义的泛型只能在该方法里使用,而接口、类声明中定义的泛型则可以在整个接口、类中使用。
  2. 方法中的泛型形参无需显式传入实际类型参数(编译器可以根据实参推断出泛型所代表的类型)
    例如下面片段:
	String [] StringArray = new String[10];
	Collection<String> CollectionString= new ArrayList<>();
	ArrayToCollection(StringArray, CollectionString);

泛型方法与类型通配符

**定义一个新的的方法:

	static  <T>  void CollectionToCollection( Collection<T> a, Collection <T> c)
	{
		for (T o : a)
		{
			c.add(o);
		}
	}

下面是一段错误的代码片段:**

	List<String> ListString=new ArrayList<>();
	List<Object> ListObject=new ArrayList<>();
	//下面代码编译错误
	CollectionToCollection(ListString,ListObject);

上面的代码片段传入了两个实际参数,数据类型分别是Object[ ]和Collection< String >,与泛型方法签名比较,会给出这样的警告:The method CollectionToCollection(Collection, Collection) in the type Test is not applicable for the arguments (List< String>, List< Object>)
编译器无法正确识别T所代表的实际类型。
为了避免错误,可以采用类型通配符的形式,参考下面代码:

import java.util.*;
public class Test {
	static  <T>  void CollectionToCollection( Collection< ? extends T> a, Collection <T> c)
	{
		for (T o : a)
		{
			c.add(o);
		}
	}

	public static void main(String[] args) {
	List<String> ListString=new ArrayList<>();
	List<Object> ListObject=new ArrayList<>();
	CollectionToCollection(ListString,ListObject);
	}

}

大多数时候,都可以用泛型方法来代替类型通配符,例如:

//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<?> c);
	...
}

上面方法使用了< T extends E>的泛型形式,这时定义泛型形参时设定上限(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){...}
}

注意::
类型通配符与泛型方法(在方法签名中显式声明泛型形参)还有一个显著的区别:类型通配符既可以在方法签名中定义形参的类型,也可以用于定义变量的类型;但泛型方法中的泛型形参必须在对应方法中显示声明.

泛型方法与泛型构造器

Java也允许在构造器签名中声明泛型形参,因此就产生了所谓的泛型构造器
定义了泛型构造器后,在使用时就可以让Java根据参数的类型来判断泛型形参的类型,或者显式地为构造器中的泛型形参指定实际的类型。
例如下面代码:

class A
{
	public <T> A(T t)
	{
		System.out.println(t);
	}
}
public class B
{
	public static void main(String[] args)
	{
		// 泛型构造器中的T类型为String。
		new A("未显式指定参数类型");
		// 泛型构造器中的T类型为Integer。
		new A(200);
		// 显式指定泛型构造器中的T类型为String,
		// 传给Foo构造器的实参也是String对象
		new <String> A("显式指定参数类型");
		// 显式指定泛型构造器中的T类型为String,
		// 但传给A构造器的实参是Double对象,下面代码出错
		//new <String> A(12.3);
	}
}

Java7新增的“菱形”语法允许调用构造器时在构造器后使用一对尖括号来代表泛型信息。但如果程序显示指定了泛型构造器中声明的泛型形参的实际类型,则不可以使用“菱形”语法。

参考下面代码:

class A<E>
{
	public <T> MyClass(T t)
	{
		System.out.println(t);
	}
}
public class Test
{
	public static void main(String[] args)
	{
		// A类声明中的E形参是String类型。
		// 泛型构造器中声明的T形参是Integer类型
		A<String> a1 = new A<>(5);
		// 显式指定泛型构造器中声明的T形参是Integer类型,
		A<String> a2 = new <Integer> A<String>(5);
		// A类声明中的E形参是String类型。
		// 如果显式指定泛型构造器中声明的T形参是Integer类型
		// 此时就不能使用"菱形"语法,下面代码是错的。
		//A<String> a3 = new <Integer> A<>(5);
	}
}

泛型方法与方法重载

因为泛型既允许设定通配符上限,也允许设定通配符的下限度,从而允许在一个类里包含多个方法定义,但使用时需要注意避免产生“陷阱”

Java8改进的类型推断

主要通过以下两个方面:
1. 可以通过调用方法的上下文来推断泛型的目标类型。
2. 可以在方法的调用链中,将推断得到的泛型传递到最后一个方法。

通过下面的代码举例:

class A<E>
{
	public static <T> A<T> nil()
	{
		return null;
	}
	public static <T> A<T> cons(T head, A<T> tail)
	{
		return null;
	}
	E head()
	{
		return null;
	}
}
public class Test
{
	public static void main(String[] args)
	{
		//可以通过方法赋值的目标参数来推断类型参数为String
		A<String> ls = A.nil();
		//无需使用下面语句在调用nil()方法时指定类型参数的类型
		A<String> mu = A.<String>nil();
		//可调用cons方法所需的参数类型来推断类型参数为Integer
		A.cons(42, A.nil());
		//无需使用下面语句在调用nil()方法时指定类型参数的类型
		A.cons(42, A.<Integer>nil());
		
		//希望系统能推断出调用nil()方法类型参数为String类型,
		//但实际推断不出,下面代码错误
		//String s = A.nil().head();
		String s = A.<String>nil().head();
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值