Effective Java笔记(30)优先考虑泛型方法

这篇具有很好参考价值的文章主要介绍了Effective Java笔记(30)优先考虑泛型方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        正如类可以从泛型中受益一般 ,方法也一样。静态工具方法尤其适合于泛型化 。 Collections 中的所有“算法”方法(例如 binarySearch 和 sort )都泛型化了 。

        编写泛型方法与编写泛型类型相类似 。 例如下面这个方法,它返回两个集合的联合 :

public static Set union(Set s1, Set s2) {
    Set result = new HashSet(s1);
    result.addAll(s2);
    return result;
}

        这个方法可以编译,但是有两条警告 :

Effective Java笔记(30)优先考虑泛型方法,Effective Java,java,开发语言,后端

        为了修正这些警告,使方法变成是类型安全的,要将方法声明修改为声明一个类型参数( type parameter ),表示这三个集合的元素类型(两个参数和一个返回值),并在方法中使用类型参数。 声明类型参数的类型参数列表,处在方法的修饰符及其返 回值之间 。 在这个示例中,类型参数列表为< E >,返回类型为 Set<E > 。 类型参数的命名惯例与泛型方法以及泛型的相同: 

public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
    Set<E> result = new HashSet<>(s1);
    result.addAll(s2);
    return result;
}

        至少对于简单的泛型方法而言,就是这么回事了 。 现在该方法编译时不会产生任何警告,并提供了类型安全性,也更容易使用 。 以下是一个执行该方法的简单程序 。 程序中不包含转换,编译时不会有错误或者警告 :

public static void main(String[] args) {
    Set<String> guys = Set.of("Tom", "Dick", "Harry");
    Set<String> stooges = Set.of("Larry","Moe", "Curly");
    Set<String> af1Cio = union(guys, stooges); .
    System.out.println(af1Cio);
}

        运行这段程序时,会打印出[ Moe, Harry , Tom , Curly , Larry , Dick ] 。 (元素的输出顺序是独立于实现的 。 )

        union 方法的局限性在于三个集合的类型(两个输入参数和一个返回值)必须完全相同 。利用有限制的通配符类型( bounded wildcard type )可以使方法变得更加灵活 。

        有时可能需要创建一个不可变但又适用于许多不同类型的对象 。由于泛型是通过擦除实现的,可以给所有必要的类型参数使用单个对象,但是需要编写一个静态工厂方法,让它重复地给每个必要的类型参数分发对象 。 这种模式称作泛型羊例工厂(generic singleton factory ),常用于函数对象,如 Collections.reverse Order ,有时也用于像 Collections.emptySet 这样的集合 。

        假设要编写一个恒等函数( identity function)分发器。 类库中提供了 Function.identity,因此不需要自己编写,但是自己编写也很有意义 。 如果在每次需要的时候都重新创建一个 ,这样会很浪费,因为它是无状态的( stateless ) 。 如果 Java 泛型被具体化了,每个类型都需要一个恒等函数,但是它们被擦除后,就只需要一个泛型单例 。 请看以下示例 :

private static UnaryOperator<Object> IDENTITY_FN = (t) -> t;

@SuppressWarnings ("unchecked")
public static <T> UnaryOperator<T> identi tyFunction() {
    return (UnaryOperator<T>) IDENTITY_FN;
}

        IDENTITY_FN 转换成( UnaryFunction<T>),产生了一条未受检的转换警告,因为UnaryFunction<Object >对于每个 T 来说并非都是个 UnaryFunction<T > 。 但是恒等函数很特殊 : 它返回未被修改的参数,因此我们知道无论 T 的值是什么,用它作为 Unary ­Function<T >都是类型安全的 。 因此,我们可以放心地禁止由这个转换所产生的未受检转换警告 。 一旦禁止,代码在编译时就不会出现任何错误或者警告 。

        下面是一个范例程序,它利用泛型单例作为 UnaryFunction<String >和 Unary­Function<Number > 。 像往常一样,它不包含转换,编译时没有出现错误或者警告 :

public static void main(String[] args) {
    String[] strings = { "jute", "hemp", "nylon" };
    UnaryOperator<String> sameString = identityFunction();
    for (String s : strings)
        System.out.printIn(sameString.apply(s));
    
    Number[] numbers = { 1,2.0, 3L };
    UnaryOperator <Number> sameNumber = identityFunction();
    for (Number n : numbers)
        System.out.println(sameNumber . apply(n)) ;
}

        虽然相对少见,但是通过某个包含该类型参数本身的表达式来限制类型参数是允许的 。这就是递归类型限制( recursive type bound ) 。 递归类型限制最普遍的用途与 Comparable接口有关,它定义类型的自然顺序 。 这个接口的内容如下 :

public interface Comparable<T> {
    int compareTo(T o);
}

        类型参数 T 定义的类型,可以与实现 Cornparable<T >的类型的元素进行比较 。 实际上,几乎所有的类型都只能与它们自身的类型的元素相比较 。 例如 String实现 Comparable<String>, Integer 实现 Comparable< Integer>,等等 。

        有许多方法都带有一个实现 Comparable 接口的元素列表,为了对列表进行排序,并在其中进行搜索,计算出它的最小值或者最大值,等等 。 要完成这其中的任何一项操作,都要求列表中的每个元素能够与列表中的每个其他元素相比较,换句话说,列表的元素可以互相比较( mutually comparable ) 。 下面是如何表达这种约束条件的一个示例 :

public static <E extends Comparable<E>> E max(Col1ection<E> C) ;

        类型限制< E extends Cornparable<E>>,可以读作“针对可以与自身进行比较的每个类型 E ”,这与互比性的概念或多或少有些一致 。

        下面的方法就带有上述声明 。 它根据元素的自然顺序计算列表的最大值,编译时没有出现错误或者警告:

pub1ic static <E extends Comparable<E>> E max(Collection<E> c) {
    if(c.isEmpty())
        throw new IllegalArgumentException("Empty collection");
    E result = null;
    for(E e:c)
        if (result == null || e.compareTo(result) > 0)
    result = Objects.requireNonNull(e);
    return result;
}

        注意,如果列表为空,这个方法就会抛出 IllegalArgumentException 异常 。 更好的替代做法是返回一个 Optional<E>  。

        递归类型 限制可能比这个要复杂得多 ,但幸运的是,这种情况并不经常发生 。 如果你理解了这种习惯用法和它的通配符变量,以及模拟自类 型( simulated selιtype )习惯用法 , 就能够处理在实践中遇到的许多递归类型限制了 。

        总而言之,泛型方法就像泛型一样,使用起来比要求客户端转换输入参数并返回值的方法来得更加安全,也更加容易 。 就像类型一样 ,你应该确保方法不用转换就能使用 ,这通常意味着要将它们泛型化 。 并且就像类型一样,还应该将现有的方法泛型化,使新用户使用起来更加轻松 ,且不会破坏现有 的客户端。文章来源地址https://www.toymoban.com/news/detail-635398.html

到了这里,关于Effective Java笔记(30)优先考虑泛型方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Effective Java笔记(6)避免创建不必要的对象

            一般来说,最好能重用单个对象,而不是在每次需要 的时候就创建一个相同功能的新对象 。 重用方式既快速,又流行 。 如果对象是不可变的( immutable ) (详见第 17 条),它就始终可以被重用 。         作为一个极端的反面例子,看看下面的语句 :    

    2024年02月15日
    浏览(36)
  • Effective Java笔记(11)覆盖 equals 时总要覆盖 hashCode

             在每个 覆盖了 equals 方法的类中,都 必须 覆盖 hashCode 方法 。 如果不这样做的话,就会违反 hashCode 的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作,这类集合包括 HashMap 和 HashSet 。 下面是约定的内容,摘自 Object 规范: 1、在应用程序的执

    2024年02月15日
    浏览(55)
  • Effective Java笔记(3)用私有构造器或者枚举类型强化 Singleton 属性

            Singleton 是指仅仅被实例化一次的类 。Singleton 通常被用来代表一个无状态的对象,如函数,或者那些本质上唯一的系统组件 。 使类成为 Singleton会使它的害户端测试变得十分困难 ,因为不可能给 Singleton 替换模拟实现,除非实现一个充当其类型的接口 。      

    2024年02月15日
    浏览(51)
  • Effective Java笔记(9)try-with-resources 优先于 try -finally

            Java 类库中包括许多必须通过调用 close 方法来手工关闭的资源 。 例如 InputStream 、OutputStream 和 java.sql.Connection 。 客户端经常会忽略资源 的关闭 ,造成严重的性能后果也就可想而知了 。 虽然这其中的许多资源都是用终结方法作为安全网,但是效果并不理想。  

    2024年02月15日
    浏览(49)
  • 【Java】泛型类,接口,方法

    泛型的作用:约束元素的类型 集合 泛型的符号: T:type E:element K:Key V:value 泛型符号是个占位符,给引用类型占位置 在使用的时候名称没有任何要求,ABCDEFG无所谓,个数也不要求 反省符号应用: 泛型类 在本类中,可以把泛型符号当成已知类型来用 泛型的具体类型在创

    2024年02月14日
    浏览(39)
  • Java笔记(集合、散列表、Map、泛型)

    set:无序不可重复 无序:不保证有序,就是有可能有序,有可能无序 不可重复:不能添加重复数据 HashSet TreeSet:底层是红黑树,会自动排序,意味着里面存储的必须是同类型的元素对象 数字:从小到大排序 字符串:一次比较每一位的ascll码值 日期:自然日期顺序 1.1.TreeS

    2024年02月19日
    浏览(41)
  • Java自定义泛型类、泛型接口、泛型方法以及 泛型擦除的细节

    体会:使用泛型的主要优点是能够在 编译时 而不是在运行时检测错误。 1.泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:E1,E2,E3 2.泛型类的构造器如下:public GenericClass(){}。而下面是错误的:public GenericClass E (){} 3.实例化后,操作原来泛型位置的结构必

    2024年02月08日
    浏览(40)
  • java 泛型作为方法的返回值的封装

    业务需要,经常需要http方式调用某服务,然后某服务返回特定类型的返回内容。 类似 String resStr = xxxHttpClient.post() ,然后它返回一个字符串,你还需要反序列化成某种格式的。 返回值可以反序列化成的形式如下: 注意,这里的这个T是个活的,它可能根据接口的内容不同而不

    2024年02月10日
    浏览(42)
  • 【JavaSE】Java进阶知识一(泛型详解,包括泛型方法,协变,逆变,擦除机制)

    目录 泛型 1. 什么是泛型 2.泛型方法 3.通配符上界(泛型的协变) 4.通配符下界(泛型的逆变) 5.泛型的编译(擦除机制)         泛型:就是让一个类能适用于多个类型,就是在封装数据结构时能让封装的类型被各种类型使用所以引入了泛型的概念,虽然有了泛型,什么数

    2024年02月04日
    浏览(44)
  • java~用泛型来定义异常类型,避免方法调用时throws异常

    我们一般在底层写方法时,如果方法显示throws异常,那么在调用时,也需要将这个异常throws出来,例如 调用时 如果调用时不显示throws出异常,编译器会报出错误,如下图 调用方法时,不需要再throws Ex了,这种对调用者更友好,如图 在第一个方法中,使用了泛型类型 来定义

    2024年02月21日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包