Java语法糖

Java语法糖,java,syntactic-sugar,Java,Syntactic Sugar,我今天遇到了这段代码,我不知道它是如何工作的。我知道如何创建匿名类,但我习惯于看到方法签名,而不仅仅是一对大括号。这些大括号之间的代码是否放在静态块中?它进入构造函数了吗?还是完全是另外一回事 conext.checking(new Expectations() { { // <- what does this pair of braces do? oneOf(alarm).getAttackAlarm(null); } }); conext.checki

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

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

{/这是一个初始化程序块。如果不看代码的其余部分,我就无法判断它的作用


技巧是将“new Expectation()”替换为“class Something extensed Expectation”。

它是一个实例初始值设定项,在所创建对象的上下文中调用代码

这相当于

Expectations exp = new Expectations();
exp.oneOf(alarm).getAttackAlarm(null);
conext.checking(exp)
不管是谁写的,他都可能认为不声明变量(不是真的)或者代码更简洁(我不同意)

这些初始值设定项的主要用途是在实例化映射时,即:

Map map = new HashMap() {{
  put("key1", "value1");   
  put("key2", "value2"); 
}};
我认为这实际上更具可读性。

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

为什么要这样做?这是构造和初始化类实例的一个单行程序技巧。例如,您有时会看到这样的情况:

new HashSet<String>(){{ add("one"); add("two"); }}
newhashset(){{{add(“一”);add(“两”);}
初始化集合的内容


缺点?因为您正在包含类中创建一个匿名类,所以该匿名类隐式地包含了对外部类的引用。这通常不是问题,但是如果(比如)您希望序列化这样构造的类。

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

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
});
private final Collection FIXED\u Collection=Collections.unmodifiableCollection(新HashSet())
{//第一组大括号声明匿名内部类
{add(1);add(2);add(3);}//第二个集合是初始值设定项块
});

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

考虑类的定义

public class Foo {
    private int i = getDefaultValue();

    private static int getDefaultValue() {
        return 5;
    }
}
对初始化
i
getDefaultValue()
的调用本质上是一个代码块,每次构造Foo实例时都会运行该代码块。符号扩展了该函数以允许更复杂的初始化

public class Foo {
    private int i;

    {
        int z = 4 + 5;
        i = z + getDefaultValue();
    }

    private static int getDefaultValue() {
        return 5;
    }
}

在JMock中使用它的方式是一种给期望一个闭包构造的外观的技巧。

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

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

我认为,这背后的主要动机是创建一个新的名称空间,在这个空间中,
Expection
中定义的名称可以更容易地被引用

例如,假设
java.lang.Math
不是final

new Math()
{{
    double x = sin(23.65);
    double y = log(x);
    ...
}};
就好像我们有了

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

它被称为“实例初始值设定项”,而不是“静态初始值设定项”(由于明显的原因,后者不会有
this
)初始化器。该块将在每次运行代码时运行,而不是像静态初始化器那样第一次运行。我相信,这个特定的示例是推荐的(由他们的文档提供的)在JMock2中实现这一点的方法。尝试让用它编写的测试代码读起来相当自然。可读,是的。但我发现这样做绝对令人讨厌(不管怎样,在测试/脚本代码之外)。它创建了一个额外的类(和类文件)dty:不正确。我只是做了一个测试类来验证这一点。构造函数在初始值设定项之前运行。静态初始值设定项在构造函数之前运行,但这不是静态初始值设定项。我必须说,在15年的Java编程中,我从来没有遇到过使用初始值设定项的情况像以前一样。这种语法并不局限于匿名类。虽然它的使用在普通类中是不必要的,而且容易混淆。+1表示注意匿名类之外的用法。我还喜欢对闭包的引用!:-)