4

メタ プログラミングを使用して Java クラスのプライベート メソッドをオーバーライドしようとしています。コードは次のようになります。

// Java class
public class MyClass{

    private ClassOfSomeSort property1;
    private ClassOfSomeOtherSort property2;

    public void init(){

        property1 = new ClassOfSomeSort();
        property2 = new ClassOfSomeOtherSort();

        doSomethingCrazyExpensive();
    }

    private void doSomethingCrazyExpensive(){
        System.out.println("I'm doing something crazy expensive");
    }
}

// Groovy class
public class MyClassTest extends Specification{

    def "MyClass instance gets initialised correctly"(){

        given:
        ExpandoMetaClass emc = new ExpandoMetaClass( MyClass, false )
        emc.doSomethingCrazyExpensive = { println "Nothing to see here..." }
        emc.initialize()
        def proxy = new groovy.util.Proxy().wrap( new MyClass() )
        proxy.setMetaClass( emc )
        when:
        proxy.init()
        then:
        proxy.property1 != null
        proxy.property2 != null     
    }
}

問題は、doSomethingCrazyExpensive のオーバーライドされた実装が呼び出されないことです。これは、プライベート メソッドが init() メソッドによって内部的に呼び出され、metaClass を介して呼び出されないためだと思います。myProxy.doSomethingCrazyExpensive() を直接呼び出すと、オーバーライドされたメソッドが呼び出されるため、メタプログラミングはある程度機能します。

内部で呼び出されたときにオーバーライドされた実装が呼び出されるように、メタ プログラミングを使用して Java クラス (またはインスタンス) のメソッドをオーバーライドする方法はありますか?

4

4 に答える 4

2

Groovyasオペレーターは非常に強力で、具体的な型からプロキシを作成できます。その変更は Java で可視化されます。悲しいことに、パブリック メソッドを変更することはできましたが、プライベート メソッドをオーバーライドできないようです。

Java クラス:

public class MyClass{

    public void init(){
        echo();
        doSomethingCrazyExpensive();
    }

    public void echo() { System.out.println("echo"); }

    private void doSomethingCrazyExpensive(){
        System.out.println("I'm doing something crazy expensive");
    }
}

グルーヴィーなテスト:

class MyClassTest extends GroovyTestCase {
    void "test MyClass instance gets initialised correctly"(){

        def mock = [
          doSomethingCrazyExpensive: { println 'proxy crazy' },
          echo: { println 'proxy echo' }
        ] as MyClass

        mock.init()

        mock.doSomethingCrazyExpensive()
    }
}

それは印刷します:

proxy echo
I'm doing something crazy expensive
proxy crazy

そのため、パブリック メソッドは、Java から呼び出された場合でも傍受されて変更されましたが、プライベート メソッドではありませんでした。

于 2014-03-27T23:56:34.957 に答える
1

metaClass を使用して、Groovy の Java コードから呼び出されたメソッドをオーバーライドすることはできません。

そのため、Java でこのプライベート メソッドの呼び出しを「モック」することはできません。これは、Groovy からではなく、Java クラス自体によって呼び出されています。

もちろん、クラスが Groovy で作成されている場合、この制限は適用されません。

可能であれば Java クラスをリファクタリングして、通常の手段を使用して高価なメソッド呼び出しをモックできるようにすることをお勧めします。または、メソッドを保護してから、サブクラスでオーバーライドすることもできます。

于 2014-03-27T20:45:44.567 に答える
0

Groovy メタプログラミングを使用して Java クラスのメソッド (パブリック メソッドであっても) を置き換えることはできないようです。Groovy コンソールで次のことを試して確認してください。

ArrayList.metaClass.remove = { obj ->
  throw new Exception('remove')
}

ArrayList.metaClass.remove2 = { obj ->
  throw new Exception('remove2')
}

def a = new ArrayList()
a.add('it')

// returns true because the remove method defined by ArrayList is called, 
// i.e. our attempt at replacing it above has no effect
assert a.remove('it')

// throws an Exception because ArrayList does not define a method named remove2, 
// so the method we add above via the metaClass is invoked
a.remove2('it')

のソースコードを変更できる場合は、保護MyClassするか、doSomethingCrazyExpensiveできればリファクタリングして、よりテストしやすいようにします

public class MyClass {

    private ClassOfSomeSort property1;
    private ClassOfSomeOtherSort property2;
    private CrazyExpensive crazyExpensive;

    public MyClass(CrazyExpensive crazyExpensive) {
        this.crazyExpensive = crazyExpensive;
    }

    public void init(){

        property1 = new ClassOfSomeSort();
        property2 = new ClassOfSomeOtherSort();

        crazyExpensive.doSomethingCrazyExpensive();
    }
}

public interface CrazyExpensive {
    public void doSomethingCrazyExpensive();  
}

上記の変更を行った後、テスト時にMyClassのモック/スタブ実装を使用して簡単にインスタンス化できますCrazyExpensive

于 2014-03-27T22:45:37.697 に答える
0

私はこの質問に出くわし、別の答えを提供する必要があると考えました: はい、既存のメソッドをオーバーライドできます。メタ クラスを ExpandoMetaClass に変更するだけです。

これは、たとえば最初のメソッドを追加すると自動的に行われます。

次に例を示します。

println ""
class Bob {
    String name
    String foo() { "foo" }
    void print() { println "$name = ${foo()} ${fum()}  metaclass=${Bob.metaClass}"}
    def methodMissing(String name, args) { "[No method ${name}]"  }
}

new Bob(name:"First ").print()

Bob.metaClass.fum = {-> "fum"}

new Bob(name:"Second").print()

Bob.metaClass.fum = {-> "fum"}

new Bob(name:"Third ").print()

Bob.metaClass.foo = {-> "Overriden Foo"}

new Bob(name:"Fourth").print()

結果は次のとおりです。

First  = foo [No method fum]  metaclass=org.codehaus.groovy.runtime.HandleMetaClass@642a7222[groovy.lang.MetaClassImpl@642a7222[class Bob]]
Second = foo fum  metaclass=groovy.lang.ExpandoMetaClass@21be3395[class Bob]
Third  = foo fum  metaclass=groovy.lang.ExpandoMetaClass@21be3395[class Bob]
Fourth = Overriden Foo fum  metaclass=groovy.lang.ExpandoMetaClass@21be3395[class Bob]

fum メソッドが追加された後、メタ クラスが expando に変更されたことがわかります。元の foo をオーバーライドしようとすると、機能します。

于 2016-02-11T00:06:26.067 に答える