【问题标题】:Java Syntactic SugarJava 语法糖
【发布时间】:2011-04-21 11:21:44
【问题描述】:

我今天遇到了这段代码,我不知道它是如何工作的。我知道如何创建匿名类,但我习惯于看到方法签名而不仅仅是一对大括号。这些大括号之间的代码是否放入静态块中?它进入构造函数吗?还是完全是别的东西?

conext.checking(new Expectations() {
    { // <- what does this pair of braces do?
        oneOf(alarm).getAttackAlarm(null);
    }
});

【问题讨论】:

    标签: java syntactic-sugar


    【解决方案1】:

    它是一个实例初始化器,在创建的对象的上下文中调用代码。

    这相当于

    Expectations exp = new Expectations();
    exp.oneOf(alarm).getAttackAlarm(null);
    conext.checking(exp)
    

    编写它的人可能认为不声明变量(不正确)或代码更简洁(我不同意)会提高效率。

    这些初始化器的主要用途是在实例化地图时,即:

    Map map = new HashMap() {{
      put("key1", "value1");   
      put("key2", "value2"); 
    }};
    

    我认为实际上更具可读性。

    【讨论】:

    • 它被称为“实例初始化器”,而不是“静态初始化器”(由于显而易见的原因,后者没有this)。另见java.sun.com/docs/books/jls/third_edition/html/classes.html#8.6
    • 它不是一个静态初始化器,只是一个(实例)初始化器。该代码块将在每次代码运行时运行,而不是像静态初始化程序那样仅在第一次运行。
    • 我相信这个具体的例子是 jMock 2 中推荐的(由他们的文档)这样做的方法。它做了很多尝试使用它编写的测试代码读起来相当自然。跨度>
    • 可读,是的。但我发现这样做绝对令人厌恶(无论如何在测试/脚本代码之外)。它为您以这种方式创建的每个地图创建一个额外的类(和类文件),占用更多的永久空间等。
    • dty:不正确。我刚刚做了一个测试类来验证这一点。构造函数在初始化程序之前运行。静态初始化器在构造函数之前运行,但这不是静态初始化器。
    【解决方案2】:

    它是一个初始化块,但不一定是 静态 初始化块。它实际上是匿名内部类的构造函数。您通常会看到这种“双括号初始化”模式来方便地创建和填充集合:

    private final Collection<Integer> FIXED_COLLECTION = Collections.unmodifiableCollection(new HashSet<Integer>() 
    { // first set of braces declares anonymous inner class
        { add(1); add(2); add(3); } // second set is initializer block
    });
    

    【讨论】:

    • 我不得不说,在 15 年的 Java 编程生涯中,我从来没有遇到过这样使用的初始化程序。
    【解决方案3】:

    这是一个实例初始化器(不是静态初始化器)。

    考虑类的定义

    public class Foo {
        private int i = getDefaultValue();
    
        private static int getDefaultValue() {
            return 5;
        }
    }
    

    初始化igetDefaultValue() 调用本质上是一个代码块,它在每次构造 Foo 的实例时运行。该符号扩展了该功能以允许更复杂的初始化。例如

    public class Foo {
        private int i;
    
        {
            int z = 4 + 5;
            i = z + getDefaultValue();
        }
    
        private static int getDefaultValue() {
            return 5;
        }
    }
    

    它在 JMock 中的使用方式是一种让期望看起来像闭包构造的技巧。

    【讨论】:

    • +1 用于注意匿名类之外的用法。我也喜欢对闭包的引用! :-)
    【解决方案4】:

    发生了什么? outer 大括号创建了一个从 Exception 派生的新匿名类。 inner 大括号定义一个初始化器并设置oneOf() 等。

    为什么要这样做?这是构造和初始化类实例的单行技巧。 e.你有时会看到这样的东西:

    new HashSet<String>(){{ add("one"); add("two"); }}
    

    初始化集合的内容。

    缺点?因为您在包含类中创建了一个匿名类,所以该匿名类隐含地包含对外部类的 this 引用。通常不是问题,但如果(例如)您想要序列化您这样构建的类,它可能会导致问题。

    【讨论】:

      【解决方案5】:

      这是一个初始化块。如果不查看其余代码,我无法判断它的作用。

      诀窍是想象“new Expectation()”替换为“class Something extends Expectation”。

      【讨论】:

        【解决方案6】:

        任何内部类都没有构造函数,因此您可以像这样定义实例初始化器,即内部大括号集是实例初始化器。

        new Expectations() { 
            { 
                oneOf(alarm).getAttackAlarm(null); 
            }
        }
        

        【讨论】:

        • 此语法不限于匿名类。虽然它的使用在常规课程中是不必要的且令人困惑。
        【解决方案7】:

        我相信,这背后的主要动机是创建一个新的名称空间,在其中可以更轻松地引用 Expection 中定义的名称。

        例如,假设java.lang.Math不是最终的,

        new Math()
        {{
            double x = sin(23.65);
            double y = log(x);
            ...
        }};
        

        好像我们有类似的东西

        with names from Math
        {
            double x = sin(23.65);
            double y = log(x);
            ...
        }
        

        【讨论】:

          猜你喜欢
          • 2012-02-07
          • 2013-07-05
          • 1970-01-01
          • 2011-01-24
          • 2015-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-09-01
          相关资源
          最近更新 更多