编程思想 -- 第15章 -- 泛型

泛型

一般的类和方法,只能使用具体的类型,要么是基本类型,要么是自定义的类。如果要编写可以应用于多种类型的编码,这种刻板的限制对代码的束缚就会很大。
在面向对象编程语言中,多态算是一种泛化机制。拘泥于单继承体系,会使程序受限太多。有时候使用接口,对程序的约束还是太强。
泛型实现了参数化类型的概念,使代码可以应用于多种类型。最初的目的是希望类或方法能够具备最广泛的表达能力,通过解耦类或方法与所使用的类型之间的约束。

一、与C++的比较
    Java中的泛型与C++比较的理由:1,了解C++的局限是什么,为什么会有这些限制。最终的目的是帮助你理解,Java泛型的边界在哪里。2,Java社区中,对C++的误解可能会误导你,令你在理解泛型的意图时产生偏差。
    
二、简单泛型
    泛型出现最引人注目的原因:为了创造容器。泛型的主要目的之一就是用来指定容器要持有什么类型的对象,而且由编译器来保证类型的正确性。
    仅一次方法调用就能返回多个对象:将一组对象直接打包存储于其中的一个单一对象,称为元组。这个容器允许读取其中元素,但是不允许向其中存放新对象。元组可以具有任意长度,元组中的对象可以是任意不同的类型。元组隐含地保持了其中元素的次序。
    一个堆栈类:LinkedList<T>和Stack<T>
    
三、泛型接口
    泛型也可以应用于接口。例如生成器(generator),这是一种专门负责创建对象的类。一般一个生成器只定义一个方法,该方法用以产生新的对象。实际上,这是工厂方法设计模式的一种应用。当使用生成器创建新的对象时,它不需要任何参数,而工厂方法一般需要参数。也就是说,生成器无需额外的信息就知道如何创建对象。
    
四、泛型方法
    泛型方法使得该方法能够独立于类而产生变化。一个基本的指导原则:无论何时,只要你能做到,就应该只使用泛型方法。
    当使用泛型类时,必须在创建对象的时候指定类型参数的值,而使用泛型方法的时候,通常不必指明参数类型,因为编译器会为我们找出具体的类型。这称为参数类型推断。
    
五、匿名内部类
    泛型还可以应用于内部类及匿名内部类。
    
六、构建复杂模型
    泛型的一个重要好处就是能够简单而安全地创建复杂的模型。
    
七、擦除的神秘之处
    在泛型代码内部,无法获得任何有关泛型参数类型的信息。java泛型使用擦除来实现,当你使用泛型时,任何具体的类型信息都被擦除,唯一知道的就是你在使用一个对象。
    泛型类型只有在静态烈性检查期间才出现,在此之后,程序中的所有泛型类型都将被擦除,替换为他们的非泛型上界。
    擦除和迁移兼容性意味着,使用泛型不是强制的。
    因为擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和离开方法的地点。这些正是编译器在编译期执行类型检查并插入转型代码的地点。
    
八、擦除的补偿
    擦除丢失了在泛型代码中执行某些操作的能力,任何情况下运行时需要知道确切类型信息的操作都将无法工作。偶尔可以绕过这些问题来编程,但是有时必须通过引入类型标签来对擦除进行补偿。这意味着你需要显示地传递你的类型的Class对象,以便你可以在类型表达式中使用它。
    不能创建泛型数组。一般的 解决方案是在任何想要创建泛型数组的地方都使用ArrayList。
    
九、边界
    边界使得你可以在用于泛型的参数类型上设置限制条件。尽管这使得你可以强制规定泛型可以应用的类型,但是其潜在的一个更重要的效果是你可以按照自己的边界类型来调用方法。
    
十、通配符
    通配符引用的是明确的类型,它意味着某个对象的引用没有指定的具体类型。
    超类型通配符<? super T>可以声明通配符是由某个特定类的任何基类来界定的。
    无界通配符<?> 意味着任何事物,无界通配符好像等价于使用原生类型。
    
十一、问题
    任何基本类型都不能作为类型参数。解决之道是使用基本类型的包装器类。
    一个类不能实现同一个泛型接口的两种变体,由于擦除的原因,这两个变体会成为相同的接口。
    使用带有泛型类型参数的转型或instanceof不会有任何效果。
    由于擦除的原因,重载方法将产生相同类型签名。
    
十二、自限定的类型
    不能直接继承一个泛型参数,但是可以继承在其自己的定义中使用这个泛型参数的类。自限定的参数可以保证类型参数必须与正在被定义的类相同。如果使用自限定,就应该了解这个类所用的类型参数将与使用这个参数的类具有相同的基类型。这会强制要求使用这个类的每个人强制遵循这种形式。
    自限定类型的价值在于它们可以产生协变参数类型--方法参数类型会随子类而变化。
    
十三、动态类型安全
    泛型容器的旧式代码仍旧可能会破坏你的容器,类型检查问题可以用java.util.Collections的静态方法CheckedCollection(),checkedList(),checkedMap(),checkedSet(),checkedSortedMap()和checkedSortedSet().这些方法每一个都会将你希望动态检查的容器当做第一个参数接受,并将你希望强制要求的类型作为第二参数接受。
    
十四、异常
    由于擦除原因,将泛型应用于异常是非常受限的。catch语句不能捕获泛型类型的异常,因为在编译时期和运行时期都必须知道异常的确切类型。反射类也不能直接或间接继承自Throwable。但是类型参数可能会在一个方法的throws字句中用到,这使得
 你可以编写随检查型异常的类型二发生变化的泛型代码。如果不能参数化所抛出的异常,由于检查型异常的缘故,不能编写出这种泛化的代码。
    
十五、混型
    混型概念是混合多个类的能力,以产生一个可以表示混型中所有类型的类,它将使组装多个类变得简单易行。
    混型的价值之一是它们可以将特性和行为一致地应用于多个类之上。如果想在混型类中修改某些东西,作为一种意外的好处,这些修改将会应用于混型所应用的所有类型之上。
    与接口混合:使用接口来产生混型效果。
    使用装饰器模式:装饰器使用分层对象来动态透明地向单个对象中添加责任。装饰器通过使用组合和形式化结构来实现,而混型是基于继承。
    与动态代理混合:通过使用动态代理,所产生的类的动态类型将会是已经混入的组合类型,由于动态代理的限制,每个被混入的类都必须是某个接口的实现。
    
十六、潜在类型机制
    潜在类型机制是一种代码组织和复用机制。有了它编写出的代码相对于与没有它写出的代码,能够更容易地复用。
    
十七、对缺乏潜在类型机制的补偿
    java不支持潜在类型机制,但是这并不意味着有界泛型代码不能再不同的类型层次结构之间应用。我们仍旧可以创建真正的泛型代码,但是这需要一些额外的努力。
    反射:通过反射能够动态地确定所需要的方法收可用并调用它们。他甚至能够处理Mime只具有一个必需的方法这一事实,并能够部分实现其目标。
    将一个方法应用于序列:反射提供了一些有趣的可能性,但是它将所有的类型检查都转移奥了运行时。如果能够实现编译器类型检查,这通常会更符合要求。
    
十八、将函数对象用作策略
    使用策略设计模式,可以产生更优雅的代码,它将变化的事物完全隔离到了一个函数对象中。
    函数对象的价值在于,与普通方法不同,它们可以传递出去,并且还可以拥有在多个调用之间持久化的状态。
    
十九、总结:转型真的如此之糟吗?
    使用泛型类型机制的最吸引人的地方,就是在使用容器类的地方,这些类包括各种List,Set,Map等中。
    泛型的通用语言特性的目的在于可表达性,而不仅仅是为了创建类型安全的容器。类型安全的容器是能够创建更通用代码这一能力所带来的副作用。
    

    
    
    
    
   

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值