It seems the groovy also support the compiling mode, using groovyc , If I run the following code with dynamic script calling way, I will get "String" method called.
Why I still got "String" even if I compiled the code using groovyc? The type of variable a is "Object", so I expected after compiling the code, I will get the "Object" function called.
Object a = "123"
def foo(Object a) {
println "Object"
}
def foo(String a) {
println "String"
}
foo(a)
Here is relevant section from groovy-docs
In Groovy, the methods which will be invoked are chosen at runtime.
This is called runtime dispatch or multi-methods. It means that the
method will be chosen based on the types of the arguments at runtime.
In Java, this is the opposite: methods are chosen at compile time,
based on the declared types.
There is a sample code in '2. Multi-methods' section, not copying here.
Finally mandatory link to MrHaki's groovy goodness page on this topic
Related
I get unexpected results from the following code snippet using the eclipse ide:
class Example(String s = "init") {
shared String a() => "Func 1";
shared String b = "Attr 1";
shared String c(Integer i) { return "Func 2"; }
}
shared
void run() {
// 1.
print("getAttributes: " + `Example`.getAttributes().string);
print("getMethods: " + `Example`.getMethods().string);
// prints: []
// i.e. doesnt print any attribute or method
// 2.
value e = Example()
type(e); // error
e.type(); // error, should be replaced by the ide with the above version.
}
ad 1.) I get as a result:
getAttributes: []
getMethods: []
where I expected lists containing attributes or methods.
ad 2.) The doc says:
"The type() function will return the closed type of the given instance, which can only be a ClassModel since only classes can be instantiated.
..."
But I cant find the type() function and others related to meta programming, i.e. I dont get a tooltip, but instead I get a runtime (!) error:
Exception in thread "main" com.redhat.ceylon.compiler.java.language.UnresolvedCompilationError: method or attribute does not exist: 'type' in type 'Example'
So, where is the equivalent to the backticks `Example`.... as a function ?
`Example`.getAttributes() returns an empty list because getAttributes takes three type arguments: Container, Get, Set. When called as getAttributes(), the typechecker tries to infer them, but since there’s no information (no arguments with the appropriate type), the inferred type argument is Nothing. Since Nothing is not a container of any of the class’ members, the resulting list is empty. Use getAttributes<>() instead to use the default type arguments, or specify them explicitly (e. g. getAttributes<Example>()). Same thing with getMethods(). Try online
The type function is in ceylon.language.meta and needs to be imported: import ceylon.language.meta { type }. Once you do that (and remove the e.type() line), the compilation error will go away.
If you directly want a meta object of the function, you can write `Example.a`.
I have the following JUnit test:
public class JavaTest {
final int value = 2;
#Test
#Repeat(times = value)
public void test() {
fail("Not yet implemented");
}
}
The #Repeat annotation comes from easytest-core, and the exact definition is here.
When I compile this as java source everything builds (and runs) fine. When I compile the exact same thing as groovy source, I get:
Groovy:Attribute 'times' should have type 'java.lang.Integer'; but found type 'java.lang.Object' in #org.easetech.easytest.annotation.Repeat GroovyTest.groovy
After searching the internets, I found a few similar discussions on SO and jira.codehaus, but those deal with String - GString problems, so the solutions do not work for me.
How can I fix this?
Updates:
java.version=1.7.0_76
groovy.version=2.3.7
Think you're bumping into the fact groovyc doesn't treat final variables as inline constants like javac does
I tried changing your int variable like this:
final Integer value = Integer.valueOf(2).intValue()
which prevents the variable from being treated as an inline constant. After that change I get a compile error from the #Repeat annotation:
Expected Integer.valueOf(2).intValue() to be an inline constant
It looks like there's some acknowledgement of the inconsistency here in a Groovy JIRA: https://issues.apache.org/jira/browse/GROOVY-1628
There's also some further discussion here in this SO thread:
Does it make sense to mark variable as final in groovy?
It doesn't look like you're going to be able to get groovy to match the Java behavior for this scenario.
I have a DSL that looks like this:
aMethod {
"a name"
"another name"
"and a third name"
}
My Problem is that I'm unable to access the three string, because calling the closure only returns the last statement. I tried to override the constructor of String(char[] value) which is called when an anonymous String-statement occurs:
def original
// primitive way to get the String(char[])-constructor
String.class.constructors.each {
if(it.toString() == "public java.lang.String(char[])") {
original = it
}
}
// overriding the constructor
String.metaClass.constructor = { char[] value ->
def instance = original.newInstance(value)
// ... do some further stuff with the instance ...
println "Created ${instance}"
instance
}
// teststring to call String(char[] value)
"teststring"
Unfortunately it didn't work and I thought anyway that it is quite complicated.
Thank you for the comments. Actually it would be great to define everything without quotes. But: After having a dsl that can be translated to java objects I'd loved to have additional annotations in my language at development time. I want to annotate duplicate names and so on. The IDE's I know better, Intellij and Eclipse handle Strings "a name" as one PSI-Elements. Splitting these elements can be very inconvinient ... I guess. I think statements in a closure like aMethod {a name} would result in an interpretation like aMethod {a.name}. That would mean that instead of having a StringLiteral Psi "a name", I would have an Object-Psi and a MethodCall-Psi or something like that. I don't know, and my next goal is just "parsing/creating" my java objects. Are you sure that it is impossible to override the String-Constructor?
Is any constructor called when you have a groovy script with this content:
"hello World"
Groovy will resolve ambiguous method calls by choosing the method with a parameter type that is alphabetically first. In the following example the method that takes a parameter of type "A" will be called if Groovy calls MyClass.printIt(null).
If you refactor this code to rename class A to class C you will find that the call with a null myVariable will then resolve to the method that takes a parameter of type "B", the bottom one.
class MyTest {
public static void main(String[] args) {
B myVariable = new B()
new MyClass().printIt(myVariable)
myVariable = null
new MyClass().printIt(myVariable)
}
}
class A {}
class B {}
class MyClass {
void printIt(A variable) {
println "TOP method called"
}
void printIt(B variable) {
println "BOTTOM method called"
}
}
the code as listed above produces the following output
BOTTOM method called
TOP method called it called the top method with a variable of type B
once class A is renamed to class C the output will change to
BOTTOM method called
BOTTOM method called
try as many permutations as you like, the method that takes the class with the lowest alphabetical name is called when the variable passed in is null, even if the variable is typed for the other class.
groovy --version
Groovy Version: 2.0.5 JVM: 1.6.0_33 Vendor: Sun Microsystems Inc. OS: Windows 7
My question is why does Groovy do this, is this by design or is it a bug, is this behavior documented anywhere?
Dave
Can't reproduce this on groovy 2.1.6. Can't reproduce this on groovy 2.0.5 either.
In both use cases output is:
BOTTOM method called
TOP method called
When A is renamed to C output is
BOTTOM method called
TOP method called
Gentoo Linux, JDK 1.7.0_25,
On my groovy (3.0.7), it's even "better":
$ groovy test.groovy
BOTTOM method called
Caught: groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method MyClass#printIt.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class A]
[class B]
groovy.lang.GroovyRuntimeException: Ambiguous method overloading for method MyClass#printIt.
Cannot resolve which method to invoke for [null] due to overlapping prototypes between:
[class A]
[class B]
at MyTest.main(test.groovy:6)
I'd like to re-implement a method of a Java class. For example, for "hi".length() to return 4. (How) Can I do that?
I know using SomeClass.metaClass I can get a reference to an existing method and define new (or overriding) method, but I can't seem to be able to do that for existing Java methods.
Using Groovy, you can replace any method (even those of final classes) with your own implementation. Method replacement in Groovy uses the meta-object protocol, not inheritance.
Here's the example you requested, i.e. how to make String.length() always return 4
// Redefine the method
String.metaClass.invokeMethod = { name, args ->
def metaMethod = delegate.metaClass.getMetaMethod(name, args)
def result = metaMethod.invoke(delegate, args)
name == 'length' ? 4 : result
}
// Test it
assert "i_do_not_have_4_chars".length() == 4
Seems like it could be possible by abusing String metaClass. But the attempt I've done so far in groovy console didn't led to the expected result :
def oldLength = String.metaClass.length
String.metaClass.length = { ->
return oldLength+10;
}
println "hi".length()
outputs the sad 2
I think you could take a look at Proxy MetaClass or Delegating metaClass.
If you did redefine it, it would only work in Groovy code. Groovy can't change the way Java code executes.
In Groovy, "hi".length() is roughly equivalent to this Java:
stringMetaClass.invokeMethod("hi","length");
Because Groovy doesn't actually call length directly, metaClass tricks work in Groovy code. But Java doesn't know about MetaClasses, so there is no way to make this work.
Although this question is very old I like to point out another way (at least for newer Groovy versions) .
The length() method in java.lang.String is implemented from java.lang.CharSequence interface. In order to reimplement the method using the String-metaClass you need to "override" the method in the metaClass of the interface first.
CharSequence.metaClass.length = { -> -1}
String.metaClass.length = { -> 4 }
assert "i_do_not_have_4_chars".length() == 4
The solution using String.metaClass.invokeMethod changes the behaviour of all String-methods and is problematic. For instance, simply invoking "asdf".size() leads to an exception on my setup.