in der starken in einer Schnittstelle definiert, ein Verfahren unter Verwendung von Ersatz-Metaklasse gebrochen ist. In diesem Fall ist das exec
-Verfahren in der Project
-Klasse definiert, bei der es sich um eine Schnittstelle handelt. Von GROOVY-3493 (berichtet ursprünglich im Jahr 2009):
"Cannot override methods via metaclass that are part of an interface implementation"
Abhilfe
invokeMethod
fängt alle Methoden und arbeiten können. Das ist Overkill, aber es funktioniert. Wenn der Methodenname mit exec
übereinstimmt, wird der Aufruf an das Objekt mySpecialInstance
umgeleitet. Andernfalls wird es an den Delegaten übergeben, nämlich die vorhandenen Methoden. Danke an invokeMethod delegation und Logging All Methods für die Eingabe auf diesem.
// This intercepts all methods, stubbing out exec and passing through all other invokes
this.project.metaClass.invokeMethod = { String name, args ->
if (name == 'exec') {
// Call special instance to track verifications
mySpecialInstance.exec((Closure) args.first())
} else {
// This calls the delegate without causing infinite recursion
MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod(name, args)
return metaMethod?.invoke(delegate, args)
}
}
Das funktioniert außer gut, dass Sie Ausnahmen über „falsche Anzahl von Argumenten“ sehen kann oder „Can not Methode xxxxx auf Null-Objekt aufrufen“. Das Problem besteht darin, dass der obige Code das Anwenden der Methodenargumente nicht verarbeitet. Für die project.files(Object... paths)
sollten die Argumente für invokeMethod das Format [['path1', 'path2']]
haben. ABER, in einigen Fällen gibt es einen Anruf an files(null)
oder files()
, so dass die Argumente für invokeMethod [null]
bzw. []
sind, die fehlschlagen, da es [[]]
erwartet. Herstellung der oben genannten Fehler.
Der folgende Code löst nur das für die files
Methode, aber das war ausreichend für meine Komponententests. Ich möchte immer noch einen besseren Weg finden, Arten zu erzwingen oder idealerweise eine einzige Methode zu ersetzen.
// As above but handle coercing of the files parameter types
this.project.metaClass.invokeMethod = { String name, args ->
if (name == 'exec') {
// Call special instance to track verifications
mySpecialInstance.exec((Closure) args.first())
} else {
// This calls the delegate without causing infinite recursion
// https://stackoverflow.com/a/10126006/1509221
MetaMethod metaMethod = delegate.class.metaClass.getMetaMethod(name, args)
logInvokeMethod(name, args, metaMethod)
// Special case 'files' method which can throw exceptions
if (name == 'files') {
// Coerce the arguments to match the signature of Project.files(Object... paths)
// TODO: is there a way to do this automatically, e.g. coerceArgumentsToClasses?
assert 0 == args.size() || 1 == args.size()
if (args.size() == 0 || // files()
args.first() == null) { // files(null)
return metaMethod?.invoke(delegate, [[] as Object[]] as Object[])
} else {
// files(ArrayList) possibly, so cast ArrayList to Object[]
return metaMethod?.invoke(delegate, [(Object[]) args.first()] as Object[])
}
} else {
// Normal pass through
return metaMethod?.invoke(delegate, args)
}
}
}
Ich bin nicht 100% sicher, dass es funktioniert, aber Sie können möglicherweise die Technik in einem anderen Beitrag in meinem Blog über überschreiben Methoden verwenden, aber immer noch die ursprüngliche Implementierung: http://naleid.com/blog/2009/06/01/groovy-metaclass-overriding-a-methode-while-using-the-old-implementation Dies könnte durch das Interface-Problem, das Sie identifiziert haben, jedoch blockiert werden ... Wenn ja, dann das Lösung, die du hast, sieht für mich wie eine gute aus. –
Dank Ted, das hat das gleiche Problem mit der Schnittstelle verhindert es zu arbeiten. Ich interessiere mich für Ihre Gedanken über Zwangstypen. Der einfache Code schlägt für 'project.files (Object ... paths)' für 'files (null)' und 'files()' fehl. Gibt es eine bessere Möglichkeit, die Typen für einen Varargs-Parameter generisch zu erzwingen? Fühlen Sie sich frei, meinen Beitrag mit Verbesserungen zu bearbeiten. – brunobowden