【自学】Java泛型程序设计-1

参考书:《Java核心技术I》+《Java编程思想》

 

  • 泛型程序设计(Generic programming) 意味着编写的代码可以被很多不同类型的对象所重用
  • 使用泛型机制编写的程序代码要比使用Object 变量,然后再进行强制类型转换的代码具有更好的安全性可读性
  • 只有当你希望使用的类型参数比某个具体类型(或者它的子类型)更加“泛化”时——当你希望代码能够跨多个类工作时,使用泛型才有所帮助

类型参数实现泛型

继承实现泛型程序设计

不用强制类型转换

编译器会进行错误检查,更安全

代码可读性更高

需要强制类型转换

没有错误检查

泛型类/方法:具有一个或多个类型变量的类/方法

类型变量

  • 类型变量常用“< >”括起来表示。类定义中的类型变量指定方法的返回类型以及域和局部变量的类型(PS:类型变量使用大写形式,且比较短,这是很常见的。在 Java 库中,使用变量 E表示集合的元素类型,K 和V分别表示表的关键字与值的类型。T (需要时还可以用临近的字母U和S )表示“任意类型”。)
  • 类型变量有时候需要用extends设置bound(限定):<T extends Coparable>表示实例化类型变量T的类必须具有Comparable类型或者是从Comparable导出的类型
  • 一个类型变量或通配符可以有多个限定,例如:T extends Comparable & Serializable。限定类型用“ &” 分隔,而逗号用来分隔类型变量
  • 在 Java 的继承中,可以根据需要拥有多个接口超类型,但限定中至多有一个类。如果用一个类作为限定,它必须是限定列表中的第一个。

泛型方法

  • 基本指导原则:无论何时,只要能做到,就应该尽量使用泛型方法(如果使用泛型方法可以取代将整个类泛型化,那就应该只使用泛型方法)
  • 定义泛型方法,只需将泛型参数列表至于修饰符的后面,返回值之前:public <T> void func(T x){};
  • 调用一个泛型方法:用具体类型替换类型变量
  • 泛型方法可以在泛型类中,也可以在非泛型类中(独立于类而产生变化)
  • static方法无法访问泛型类的类型参数,如果static方法需要使用泛型能力,就必须成为泛型方法
  • 使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器可以通过“类型参数推断”找出具体的类型,因此可以像调用普通方法一样调用泛型方法
  • 类型推断只对赋值操作有效,其他时候不起作用

类型参数的擦除

  • 虚拟机中没有泛型,只有普通的类和方法。所有的类型参数都用它们的限定类型替换。
  • 泛型类型参数将被擦除到它的第一个bound,编译器会将类型参数替换为它的擦除
  • 在基于擦除的实现中,泛型类型被当作第二类类型处理,即不能在某些重要的上下文环境中使用的类型。
  • 泛型类型只有在静态类型检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为它们的非泛型上界,例如List<T>这样的类型注解将被擦除为List,而普通的类型变量在未指定bound的情况下将被擦除为Object
  • 擦除的核心动机是使得泛化的客户端可以通过非泛化的类库来使用,反之亦然,这经常被称为“迁移兼容性”(设计 Java 泛型类型时, 主要目标是允许泛型代码和遗留代码之间能够互操作)
  • 擦除不是一个语言特性,而是Java的泛型实现中的一种折中:Java泛型不仅必须支持向后兼容性,即现有的代码和类文件仍旧合法,并且继续保持之前的含义;而且还有支持迁移兼容性,使得类库按照它们自己的步调变为泛型的,并且当某个类库变为泛型时,不会破坏依赖于它的代码和应用程序。因此擦除是唯一可行的解决方案,即通过允许非泛型与泛型代码共存,使得这种向着泛型的迁移成为可能。
  • 即使擦除在方法或类内部移除了有关实际类型的信息,编译器仍旧可以确保在方法或类中使用的类型的内部一致性
  • 限定bound是编译器在编译期执行类型检查并插入转型代码的地点。泛型中的所有动作都发生在限定处(对传递进来的值进行额外的编译期检查操作,并插入对传递出去的值的转型操作)

擦除的代价

  • 泛型不能用于显式地引用运行时类型的操作之中,即任何值运行时需要知道确切类型信息的操作都将无法工作,例如转型、instanceof、new

擦除的补偿

  • 可以通过引入类型标签来对擦除进行补偿,即显示地传递类型的Class对象。编译器将确保类型标签可以匹配泛型参数

1. 创建类型实例

  • 上面Erased.java中创建一个new T()失败,部分原因是因为擦除,另一部分原因是因为编译器无法验证T具有默认(无参)构造器
  • Java解决办法:给构造函数传递一个工厂对象,用其来创建新的实例。(最便利的工厂对象就是Class对象)

  

  • 还可以给泛型类设置class对象的域,这样不仅能用new创建类型实例,还可以使用isInstance()

  

  • 不止可以通过Class<T>传递工厂对象,还可以创建显式的工厂对象,用于限制要替换类型参数的实际类的类型(限制其必须具备的功能等)

 

2. 泛型数组

  • 如Erased.java所示,不能创建泛型数组。一般的解决方案是在任何想要创建泛型数组的地方都使用ArrayList

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值