zoukankan      html  css  js  c++  java
  • 谜题71 :进口税

    在 5.0 版中,Java 平台引入了大量的可以使操作数组变得更加容易的工具。下面这个谜题使

    用了变量参数、自动包装、静态导入(请查看 http://java.sun.com/j2se/5.0/docs/guide/language
    [Java-5.0])以及便捷方法 Arrays.toString(请查看谜题 60)。那么,这个程序会打印什么呢?

    import static java.util.Arrays.toString;
    class ImportDuty {
    public static void main(String[ ] args) {
    printArgs(1, 2, 3, 4, 5);
    }
    static void printArgs(Object... args) {
    System.out.println(toString(args));
    }
    }
    

      


    你可能会期望该程序打印[1,2,3,4,5],实际上它确实会这么做,只要它能编译。
    令人沮丧的是,看起来编译器找不到恰当的 toString 方法:

    ImportDuty.java:9:Object.toString()can't be applied to(Object[])
    System.out.println(toString(args));
    

      


    是不是编译器的理解力太差了?为什么它会尝试着去应用 Object.toString()
    呢?它与调用参数列表并不匹配,而 Arrays.toString(Object[ ])却可以完全
    匹配。
    编译器在选择在运行期将被调用的方法时,所作的第一件事就是在肯定能找到该
    方法的范围内挑选[JLS 15.12.1]。编译器将在包含了具有恰当名字的方法的最
    小闭合范围内进行挑选,在我们的程序中,这个范围就是 ImportDuty 类,它包
    含了从 Object 继承而来的 toString 方法。在这个范围中没有任何可以应用于
    toString(args)调用的方法,因此编译器必须拒绝该程序。
    换句话说,我们想要的 toString 方法没有在调用点所处的范围内。导入的
    toString 方法被 ImportDuty 从 Object 那里继承而来的具有相同名字的方法所
    遮蔽(shade)了[JLS 6.3.1]。遮蔽与遮掩(谜题 68)非常相像,二者的关键
    区别是一个声明只能遮蔽类型相同的另一个声明:一个类型声明可以遮蔽另一个
    类型声明,一个变量声明可以遮蔽另一个变量声明,一个方法声明可以遮蔽另一
    个方法声明。与其形成对照的是,变量声明可以遮掩类型和包声明,而类型声明
    也可以遮掩包声明。
    当一个声明遮蔽了另一个声明时,简单名将引用到遮蔽声明中的实体。在本例中,
    toString 引用的是从 Object 继承而来的 toString 方法。简单地说,本身就属
    于某个范围的成员在该范围内与静态导入相比具有优先权。这导致的后果之一就
    是与 Object 的方法具有相同名字的静态方法不能通过静态导入工具而得到使
    用。
    既然你不能对 Arrays.toString 使用静态导入,那么你就应该用一个普通的导入
    声明来代替。下面就是 Arrays.toString 应该被正确使用的方式:

    import java.util.Arrays;
    class ImportDuty {
    static void printArgs(Object... args) {
    System.out.println(Arrays.toString(args));
    }
    }
    

      


    如果你特别强烈地想避免显式地限定 Arrays.toString 调用,那么你可以编写你
    自己的私有静态转发方法:

    private static String toString(Object[] a) {
    return Arrays.toString(a);
    }
    

      


    静态导入工具所专门针对的情况是:程序中会重复地使用另一个类的静态元素,
    而每一次用到的时候都进行限定又会使程序变得乱成一锅粥。在这类情况中,静
    态导入工具可以显著地提高可读性。这比通过实现接口来继承其常量要安全得
    多,而实现接口这种做法是你从来都不应该采用的 [EJ Item 17]。然而,滥用
    静态导入工具也会损害可读性,因为这会使得静态成员的类在何处被使用显得非
    常不清晰。应该有节制地使用静态导入,只有在非常需要的情况下才应该使用它
    们。
    对 API 设计者来说,要意识到当某个方法的名字已经出现在某个作用域内时,静
    态导入工具并不能被有效地作用于该方法上。这意味着静态导入不能用于那些与
    通用接口中的方法共享方法名的静态方法,而且也从来不能用于那些与 Object
    中的方法共享方法名的静态方法。再次说明一下,本谜题所要说明的仍然是你在
    覆写之外的情况中使用名字重用通常都会产生混乱。我们通过重载、隐藏和遮掩
    看清楚了这一点,现在我们又通过遮蔽看到了同样的问题。

  • 相关阅读:
    JavaBean对象与Map对象互相转化
    PowerDesigner V16.5 安装文件 及 破解文件
    eclipse get set 自动添加注释
    严重: Error configuring application listener of class org.springframework.web.context.ContextLoaderListener
    java poi excel操作 把第一列放到最后去
    java poi excel操作 下拉菜单 及数据有效性
    maven 项目编译失败
    关于TbSchedule任务调度管理框架的整合部署
    mat 使用 分析 oom 使用 Eclipse Memory Analyzer 进行堆转储文件分析
    tomcat启动问题,卡在 preparing launch delegate 100% 的解决方法
  • 原文地址:https://www.cnblogs.com/yuyu666/p/9841017.html
Copyright © 2011-2022 走看看