0

私はGroovyの基本を理解しています-そしてクロージャ...

私はJavaからgroovyを呼び出そうとしています:

// Patient.java

public class Patient {  
    //... other data  
    private Map<String, String> attribStore = new HashMap<String,String>();  
    // getters/setters for attribStore omitted  

    public void addAttribute(String key, String val) {
        if (!attribStore.containsKey(key)) {
            attribStore.put(key, val);
        }
    }

// GroovyHelper.java

public class GroovyHelper {
    private String codeSnippet; // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        addMethodMissingHandler();
        GroovyShell shell = createGroovyShell(binding);
        Object result = shell.evaluate(codeSnippet);
        return result.toString();
    }

    // installs a patient in the binding - accesses the patient  
    // attribStore from groovy
    // The missing method is used to create an "attribute" i.e.
    // a (key,val) pair in the patient map 
    private void addMethodMissingHandler() {
        codeSnippet = "def attribStore = p.getAttribStore();\n"
        + "Patient.metaClass.methodMissing = \n{"
        + " String methodName, args -> \n"
        + "methodName = methodName.replaceFirst(/^get/, '');\n"  
        + "def attrib = methodName[0].toLowerCase() + methodName.substring(1);\n"
        + "if (!attribStore.containsKey(attrib)) { attribStore[attrib] = '0'; }\n"
        + "return attribStore[attrib]; \n" + "}\n" + codeSnippet;
    }  
}

//junitテストコード

private Patient p;
private Binding binding;
private GroovyHelper gh;

    @Before 
    public void init() {
        p = new PatientBuilder().build();
        binding = new Binding();
        binding.setVariable("p", p);
        gh = new GroovyHelper();
    }

    @Test //passes
    public void testPopulatePatientAttribStore() throws Exception {
        p.addAttribute("xyz", "4");
        gh.setCodeSnippet("p.getXyz()");
        gh.evaluateSnippetToString(binding);
    }

    @Test
    public void testGroovy() throws Exception {
        Binding binding = new Binding();
        binding.setVariable("p", new Patient()); // new patient
        p.addAttribute("xyz", "9");

        GroovyShell gs1 = new GroovyShell(binding);

        assertEquals("9", gs1.evaluate("p.getXyz()")); // fails??? - expected: <[9]> but was: <[4]>
    }

私の質問は-クロージャは以前のバインディングの属性ストアを保持していますか?
ここで何が起こっているのですか?
長いコードについてお詫びします-私はそれに取り組みました
-無関係なコードを削減し
て最小限に縮小します-ポインタ、「もっと読むべき」ヒントはありますか?

4

2 に答える 2

1

問題の一部は、特定の患者の に委譲するメタクラスで をmethodMissing作成していることだと思います。私は別の方法で問題に取り組みます-クラス自体に直接実装するオプションですか?PatientattribStoremethodMissingPatient

public class Patient {
  // other members as before

  public Object methodMissing(String name, Object[] args) {
    if(name != null && name.startsWith("get") && name.length() > 3) {
      String attrName = name.substring(3,1).toLowerCase() + name.substring(4);
      addAttribute(attrName, "0");
      return attribStore.get(attrName);
    } else {
      throw new MissingMethodException(name, this.getClass(), args);
    }
  }
}

または、これがオプションでない場合はGroovyHelper、Groovy でクラスを実装できますか (groovyc を使用してコンパイルすると、Java クラスと同じように Java から呼び出すことができます)。

public class GroovyHelper {
    static {
      // add a methodMissing to the Patient metaclass
      Patient.metaClass.methodMissing = { String name, args ->
        if(name?.startsWith("get") && name.length() > 3) {
          String attrName = name.substring(3,1).toLowerCase() + name.substring(4)
          // delegate here is the particular Patient instance on which the
          // missing method call was made
          delegate.addAttribute(attrName, "0")
          return delegate.attribStore[attrName];
        } else {
          throw new MissingMethodException(name, this.getClass(), args);
        }
      }
    }

    private String codeSnippet // groovy script code

    public String evaluateSnippetToString(Binding binding) {
        GroovyShell shell = createGroovyShell(binding)
        Object result = shell.evaluate(codeSnippet)
        return result.toString()
    }
}
于 2012-12-28T20:37:17.840 に答える
0

確かに、MetaClass forPatientは b/wGroovyShellの実行に耐えているようです。MetaClassRegistryはシングルトンであり、クラスローダーまたは JVM の存続期間中存続する可能性があります (現時点では、これが JVM でどのように機能するかはよくわかりません)。そして、あなたが示唆するように、クロージャは最初に取得した変数methodMissingの周りで閉じていると思います。attribStore(簡単なテストでは、そのクロージャー内からバインディング内にアクセスできる必要があることが示されているようpです。それを試してみて、うまくいかなかったと思いますか?)。1 つのオプションは、teardown メソッドで各テストの後に MetaClass を強制的に削除することです。Patient

于 2012-12-28T20:34:14.537 に答える