Why not disallow overriding and overloading of methods using the meta class for methods defined at compile time?
Can anyone foresee which real world use case can benefit from dynamic overriding of POJO methods? Probably not. So I really do not want to speak for being that restrictive.
[...] And now answer me why Java does still allow the user to overload equals?
OK, Java allows overloading equals. Groovy allows dynamic overriding of equals. Both probably makes little sense, but the user has to know what she or he is doing. The explanation that dynamic aspects don't work in the Java domain may suffice. It is a valid point of view.
But what about the byteValue(), doubleValue(), floatValue(), intValue(), longValue() and shortValue() of Number and the toString() methods of String, StringBuilder and StringBuffer?
Let's suppose we want to dynamically override the Integer semantics to cover non-negative integers only. The arithmetic should be as usual, except the minus operation which should be defined as x - y := ||x| - |y||:
Integer.metaClass.intValue = {->
delegate.value < 0 ? -delegate.value : delegate.value
}
Integer.metaClass.toString = {->
Integer.toString(delegate.intValue())
}
Integer.metaClass.equals = {Object other ->
assert false : "never called by this script"
}
Integer.metaClass.compareTo = {Object other ->
assert false : "never called by this script"
}
assert 0.intValue() == 0
assert 42.intValue() == 42
assert (-42).intValue() == 42
assert 0.toString() == "0"
assert 42.toString() == "42"
assert (-42).toString() == "42"
assert -1 != 1
assert new Integer(-1) != new Integer(1)
Integer i
i = 3 + -4
assert i == -1 assert i.toString() == "1"
i = -4 + 3
assert i == -1 assert i.toString() == "1"
assert Math.max(1, -2) == 1
sw = new StringWriter()
out = new PrintWriter(sw, true)
print(-42)
assert sw.toString() == "-42"
The above script runs without assertion errors. This raises some questions.
1) How would Integer#intValue() be dynamically overridden in future Groovy versions that are supposed to respect the private modifier on fields and methods?
2) Should DefaultTypeTransformation#compareEqual respect the new intValue()? [2] is pure Groovy, so it probably should.
3) Should any GDK method respect the new intValue()? Again, [3] is pure Groovy, so it probably should. Note that [3a] together with [3b] requires that both the lhs and rhs operands go through the MOP to get the value.
4) Math.max(1, -2) == 2? Here the Java domain ignores the dynamic aspect, so it's probably ok that we have 1 as the result. If the new intValue() should be respected here, then probably all unboxing methods had to go through the MOP.
5) Should GroovyObjects that are part of the Groovy API or runtime respect the new intValue()? [5] is pure Groovy, so they probably should.
6) What about return values and boxing? Probably there are no changes required.
I think support - even at a low level - for dynamic overriding of these methods would become rather expensive (even more if String
{Builder,Buffer}
.toString() is overridden). Maybe too expensive compared to it's benefit, since overriding is a really rare case for this methods and there are better ways to solve problems as the one above.
let us discuss this a bit please... First... there is always a static and dynamic aspect of a class. We can change the dynamic, but not the static aspect. Javacode will normally only act on the static aspects. This is true especially for classes from java.lang, but is a general "problem". For example if you create a class in Groovy or Java and then call a method that exists in the static aspect from Java, then the method in the static aspect will be called, totally ignoring the MetaClass and with it any dynamic aspect. The only way to change that right now would be by changing the bytecode itself, which is possible sometimes, but is not a way we want to go. Your last point is invalid, because the current way is aöready a way that respects dynamic methods. For example for "a"+"b" we could have theoretically an alternative plus implementation, which may lead to a different result than "ab".
When you say "don't overwrite" I guess you actually mean the GDK methods too. But why are interfaces in the list? Any method bound to that interface would normally be overwritten by the actual implementation of the interface anyway.
As for your point "performance"... it is kind of funny, because we already decided to go away from the pure the static way in GDK methods we try to follow most of the time. For example equals... even if equals(Object) is locked for changes, you can still overload the method by using a more specific parameter type, thus requiring dynamic dispatch because of multi methods. It was a requirement to enable overloaded equals methods in GDK methods. But if we go with equals, then why not with the others too? You see we are trying to improve Groovy's performance big times, but the ultimate goal is still to have most of Groovy written in Groovy itself, especially the GDK methods. We don't fear the performance, because as with 1.6 we have ways to improve and we continue working on that part.
So if your only reason for "doesn't make sense" is performance then the argument doesn't make sense to me. Your point 3 is a general mismatch between Java and Groovy and is owed to the fact that we cannot change methods as we like for Java. Which is more or less your point 4.
So I guess you need to explain "doesn't make sense" a bit more.