Java 泛型

匿名 (未验证) 提交于 2019-12-02 21:53:52

Java 泛型
-------------------------------------------------------------------------------------------------------------------
JDK 5 增加泛型支持在很大程度上都是为了让集合能记住其元素的数据类型。
Java 5 以后,Java 引入了 “参数化类型 (parameterized type)” 的概念, Java 的参数化类型被称为泛型 ( Generic )

public class GenericList
{










}

Java 7 泛型的“菱形”语法
------------------------------------------------------------------------------------------------------------------
从 Java 7 开始, Java 允许在构造器后不需要带完整的泛型信息,只要给出一对尖括号 (<>) 即可, Java 可以推断尖括号里应该是什么泛型信息。
例如:



*
*
*

2 深入泛型
--------------------------------------------------------------------------------------------------------------------
所谓泛型,就是允许在定义类、接口、方法时使用类型形参,这个类型形参将在声明变量、创建对象、调用方法时动态地指定(即传入实际的类型参数,也可以称为类型实参)。
Java 5 改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,从而可以在声明集合变量、创建集合对象时传入类型实参。例如: List<String>, ArrayList<String>

// 定义接口时指定了一个类型形参,该形参名为 E
public interface List<E>
{




}
泛型的实质:允许在定义接口、类时声明类型形参,类型形参在整个接口、类体内可当成类型使用,几乎所有可使用普通类型的地方都可以使用这种类型形参。

包含泛型声明的类型可以在定义变量、创建对象时传入一个类型实参,从而可以动态地生成无数多个逻辑子类,但这种子类在物理上并不存在。

可以为任何类、接口增加泛型声明,并不是只有集合类才可以使用泛型声明,虽然集合类是泛型的重要使用场所。

// 定义Apple类时使用了泛型声明
public class Apple<T>
{

























}
当创建带泛型声明的自定义类,为该类定义构造器时,构造器名还是原来的类名,不要增加泛型声明,例如,为 Apple<T>类定义构造器,其构造器名依然是Apple,
而不是 Apple<T>, 调用该构造器时可以使用 Apple<T> 的形式,当然应该为 T 形参传入实际的类型参数。 Java 7 提供了菱形语法,允许省略 <> 中的类型实参。



从泛型类派生子类
----------------------------------------------------------------------------------
当创建了带泛型声明的接口、父类之后,可以为该接口创建实现类,或从该父类派生子类,需要指出的是,当使用这些接口、父类时不能再包含类型形参

下面代码是错的:



定义类、接口、方法时可以声明类型形参,使用类、接口、方法时应该为类型形参传入实际的类型。
如果想从 Apple 类派生一个子类,则可以改为如下代码:



使用类、接口时也可以不为类型形参传入实际的类型参数,即下面的代码也是正确的。



并不存在泛型类:








*
*
*

3 类型通配符
---------------------------------------------------------------------------------------------------------------
如果 Foo 是 Bar 的一个子类型 (子类或子接口),而 G 是具有泛型声明的类或接口, G<Foo> 并不是 G<Bar> 的子类型。
数组和泛型有所不同,假设 Foo 是 Bar 的一个子类型 (子类或者子接口),那么 Foo[] 依然是 Bar[] 的子类型。

使用类型通配符:











设定类型通配符的上限:
----------------------------------------------------------
//表示所有Shape泛型List的父类
List <? extends Shape>

此处的问号(?) 代表一个未知的类型,但此处的这个未知类型一定是 Shape 的子类型 (也可以是 Shape 本身), 因此可以把 Shape 称为这个通配符的上限 ( upper bound )

设定类型形参的上限:
----------------------------------------------------------
























*
*
*

4 泛型方法
---------------------------------------------------------------------------------------------------
前面介绍了在定义类、接口时可以使用类型形参,在该类的方法定义和成员变量定义、接口的方法定义中,这些类型形参可以被当成普通类型来用。
在另外一些情况下,定义类、接口时没有使用类型形参,但定义方法时想自己定义类型形参,这也是可以的, Java 5 提供了对泛型方法的支持。

定义泛型方法 (Generic Method) :所谓泛型方法, 就是在声明方法时定义一个或多个类型形参。 格式如下:






泛型方法的方法签名比普通方法的方法签名多了类型形参声明,类型形参声明以尖括号括起来,多个类型形参之间以逗号(,)隔开,所有的类型形参声明放在方法修饰符和方法返回值类型之间。

public class GenericMethodTest
{







































上面程序定义了一个泛型方法,该方法中定义了一个 T 类型形参,这个 T 类型形参就可以在该方法内当成普通类型使用。
与接口、类声明中定义的类型形参不同的是,方法声明中定义的形参只能在该方法里使用,而接口、类声明中定义的类型形参则可以在整个接口、类中使用。

与类、接口中使用泛型参数不同的是,方法中的泛型参数无须显式传入实际类型参数,当程序调用 fromArrayToCollection() 方法时,无须在调用方法前传入 String、Object等类型,但系统
依然可以知道类型形参的数据类型,因为编译器根据实参推断类型形参的值,它通常推断出最直接的类型参数。


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
















Java 7 的 菱形 语法与泛型构造器
-------------------------------------------------------
正如泛型方法允许在方法签名中声明类型形参一样,Java 也允许在构造器签名中声明类型形参,这样就产生了所谓的泛型构造器。
一旦定义了泛型构造器,接下来在调用构造器时,就不仅可以让 Java 根据数据参数的类型来 “推断” 类型参数的类型,而且程序员也可以显式地为构造器中的类型形参指定实际的类型。


class Foo
{




}
public class GenericConstructor
{













}

前面介绍过 Java 7 新增的 菱形 语法,它允许调用构造器时在构造器后使用一对尖括号来代表泛型信息,但如果程序显式指定了泛型构造器中声明的类型形参的实际类型,则不可以使用 菱形语法。
class MyClass<E>
{




}
public class GenericDiamondTest
{












}

设定通配符下限:
--------------------------------------------------------------------------------
<? super Type> : 这个通配符表示它必须是 Type 本身,或是 Type 的父类。

public class MyUtils
{






















}

Java 8 改进的类型推断
-------------------------------------------------------



class MyUtil<E>
{












}
public class InferenceTest
{
















}

5 擦除和转换
-----------------------------------------------------------------------------------------------------------------
在严格的泛型代码里,带泛型声明的类总应该带着类型参数。但为了与老的 java 代码保持一致,也允许在使用带泛型声明的类时不指定实际的类型参数。
如果没有为这个泛型类指定实际的类型参数,则该类型参数被称为 raw type (原始类型),默认是声明该类型参数时指定的第一个上限类型。

当把一个具有泛型信息的对象赋值给另一个没有泛型信息的变量时,所有在尖括号之间的类型信息都将被扔掉。比如 List<String> 类型转换为 List, 则该 List 对集合元素的类型检查变成了类型参数的上限(即 Object)。

class Apple<T extends Number>
{
















}
public class ErasureTest
{












}

Java 允许直接把一个 List 对象赋值给一个 List<Type> (Type 可以是任何类型)类型的变量,只是发出 “未经检查的转换” 警告。
但对 list 变量实际上引用的是 List<Integer>集合,所以当试图把集合里的元素当成 String 类型的对象取出时,将引发运行时异常。

public class ErasureTest2
{











}

文章来源: Java 泛型
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!