这篇文章包括:
Java 7的“菱形”语法和通用生成器定义了通用方法的通用方法和类型通配符之间的区别,通配符下限通用方法和方法重载Java 8的改进的类型估计1 .通用方法的定义应该是对象数组要实现这一方法,请考虑以下代码:
上面定义的方法没有任何问题。 重要的是方法中的c参数,其数据类型为Collection。 正如前面所述,Collection不是Collection的子类型。 因此,此方法的功能有限,只能将Object[]数组中的元素复制到名为Object[] (对象子类不行)的元素的Collection集合中。 也就是说,下面的代码会引起问题。
可见,上述方法的参数类型不能使用Collection。
使用通配符Collection? 不行。 因为Java不允许将对象放入未知类型的集合中。
要解决此问题,请在声明方法时使用Java 5提供的常规方法定义一个或多个类型参数。 常用的使用方法如下
泛型方法的方法签名比常规方法的方法签名多类型参数声明,类型参数声明用尖括号括起来,多个类型参数直接用逗号(,)分隔,所有类型参数声明都位于方法修饰符和返回类型之间。
使用支持泛型的方法,可以按如下方式重写上面的fromArrayToCollection方法:
以下步骤说明了完整的使用方法。
上述程序调用了定义t型参数的通用方法。 此通用方法允许将此t型参数作为方法中的常规类型使用。 与接口、类声明中定义的类型参数不同,在方法声明中定义的类型参数只能在该方法中使用,但在接口、类声明中定义的类型参数可以在整个接口和类中使用。
与类不同,与接口的通用参数不同,方法的通用参数不必显式传递给实际类型参数。 正如上面的程序所示,当程序调用fromArrayToCollection ()方法时,在调用方法之前不需要传递String、Object等类型,但编译器知道实际参数的数据类型
上面代码中的cs是Collection类型,它与定义方法时的fromarraytocollection(t[]a,Collection c )进行比较。 如果只比较常规参数,则可以看到此t形形状参数表示的实际类型是字符串类型。
对于以下调用代码:
上面的cn是Collection类型,与此方法的方法签名进行比较。 如果只比较通用参数,则可以看到此t型参数表示Number型。
要执行以下操作:
将上一个集合中的元素复制到下一个集合的test )方法在上一个程序中定义。 在此方法中,两个形状参数from、to的类型为Collection,因此调用方法时两个集合实例的泛型类型必须相同。 否则,编译器将无法准确估计通用方法的类型参数类型。
在上述步骤中,定义了将集合中的元素复制到下一个集合中的test ) )方法。 在此方法中,两个形状参数from、to的类型为Collection,因此调用方法时两个集合实例中的泛型类型必须相同。 否则,编译器将无法准确估计泛型方法中类型参数的类型。
在上述程序中调用test方法时,将传递两个实际参数。 其中,as的数据类型为List,ao的数据类型为List,与通用方法签名进行比较。 测试(Collection c、Collection c和编译器无法正确识别t表示的实际类型。
要避免此错误,请将方法更改为:
2 .通用方法与类型通配符的区别往往是通用方法可以代替类型通配符。 例如,Java的Collection接口有两个方法定义:
上面集合中两个方法的形状参数都采用类型通配符的形状,如下所示: 或者,采用通用方法的形式。
上述方法使用的是通用型。 在这种情况下,在定义类型参数时设置上限。 其中e是在Collection接口中定义的类型参数,在该接口中,e可以用作常规类型。
在上述两种方法中,类型参数t仅使用了一次。 类型参数t带来的唯一效果是在不同的调用点传递不同的实际类型。 在这种情况下,必须使用通配符。 通配符旨在支持灵活的子类化。
常规方法允许使用类型参数表示方法的一个或多个参数之间的依赖关系,或者方法的返回值和参数之间的类型依赖关系。 如果没有这种类型依赖关系,就不应该使用类型方法。
如果需要,还可以将通配符与通用方法(如Java的Collections.copy ()方法)一起使用。
3. Java 7菱形语法和通用生成器通用生成器可以在生成器签名中声明类型参数,以便在调用生成器时根据数据类型推断类型参数的类型。 程序员还可以在生成器中的类型参数中明确指定实际类型。
4 .设置通配符下限,假设自己实现一种工具方法。 实现src设置
合里的元素复制到 dest 集合里的功能,因为 dest 集合可以保存 src 集合里的所有元素,所以 dest 集合元素里的类型应该是 src 集合元素类型的父类。为了表示两个参数之间的类型依赖,可以考虑同时使用通配符、泛型参数来实现这些方法。上面方法实现了前面的类型。现在假设该方法需要一个返回值,返回最后一个被复制的元素,则可以把上面方法改为如下形式:
在遍历 src 集合元素时,src 元素的类型是不确定的(只可以肯定它是 T 的子类),程序只能用 T 来笼统表示各种 src 集合的元素类型。
使用这种语句,可以保证程序的 1 处调用后推断出最后一个被复制的元素类型是 Integer,而不是笼统的 NUmber 类型。
实际上, Java 集合框架中的 TreeSet 有一个构造器也用到了这种设定通配符下限的语句,如下所示:
TreeSet 会对集合中的元素按自然顺序或定制顺序进行排序。如果需要 TreeSet 对集合中的所有元素进行定制排序,则要求 TreeSet 对象有一个与之关联的 Comparator 对象。上面构造器中的参数 c 就是进行定制排序的 Comparator 对象。
Comparator 接口也是一个带泛型声明的接口:
通过这种带下限的通配符的语法,可以在创建 TreeSet 对象时灵活选择合适的 Comparator 。假定需要创建一个 TreeSet 集合,并传入一个可以比较 String 大小的 Comparator,这个 Comparator 既可以是 Comparator,也可以是 Comparator —只要尖括号里传入的类型是String 的父类型(或它本身)即可。
通过使用这种通配符下限的方式来定义 TreeSet 构造器的参数,就可以将所有可用的 Comparator 作为参数传入,从而增加了程序的灵活性。当然,不仅 TreeSet 有这种用法,TreeMap 也有类似的用法。 5. 泛型方法与方法重载
因为泛型既允许设定通配符的上限,也允许设定通配符的下限,从而允许在一个类里包含如下两个方法定义:
上面的 MyUtils 类中包含两个 copy() 方法,这两个方法的参数列表有区别。
类型推断主要有如下两个方面:
可通过调用方法的上下文来推断类型参数的目标参数可在方法调用链中,将推断得到的类型参数传递到最后一个方法。上面程序中前两行粗体字代码的作用完全相同,但第一行代码无须在调用 MyUtil 类的 nil()方法时显式指定类型参数为 String ,这是因为程序需要将该方法的返回值赋值给 MyUtil类型,因此系统可以自动推断出此处的类型参数为 String 类型。
上面程序中第 3 行与第 4 行粗体字代码的作用也完全相同,但第 3 行粗体字代码也无须在调用 MyUtil 类的 nil() 方法时显式指定类型参数为 Integer,这是因为程序将 nil() 方法的返回值作为了 MyUtil 类的 cons() 方法的第二个参数,而程序可以根据 cons() 方法的第一个参数(42)推断出此处的类型参数为 Integer 类型。
虽然 Java 8 增强了泛型推断的能力,但也不是万能的。下面代码就是错的:
因此,上面这行代码必须显式指定类型参数,即将代码改为如下形式: