Esto es lo que dice la wikipedia sobre Method overriding:
Method overriding, in object oriented programming, is a language feature that allows a subclass to provide a specific implementation of a method that is already provided by one of its superclasses. The implementation in the subclass overrides (replaces) the implementation in the superclass.
A subclass can give its own definition of methods which also happen to have the same signature as the method in its superclass. This means that the subclass's method has the same name and parameter list as the superclass's overridden method. Constraints on the similarity of return type vary from language to language, as some languages support covariance on return types.
Method overriding is an important feature that facilitates polymorphism2.1 in the design of object-oriented programs.
Some languages allow the programmer to prevent a method from being overridden, or disallow method overriding in certain core classes. This may or may not involve an inability to subclass from a given class.
In many cases, abstract classes are designed — i.e. classes that exist only in order to have specialized subclasses derived from them. Such abstract classes have methods that do not perform any useful operations and are meant to be overridden by specific implementations in the subclasses. Thus, the abstract superclass defines a common interface which all the subclasses inherit.
En el caso de los operadores, un término mas convencional que redefinición o sobreescritura (overriding) es overloading o sobrecarga de operadores [1], [2]. La diferencia es que sobrecarga sugiere que hay múltiples instancias de un método (y por tanto del correspondiente operador asociado) que sólo difieren en el tipo de sus parámetros.
Citemos la documentación de groovy en http://groovy.codehaus.org/Operator+Overloading:
Groovy supports operator overloading which makes working with Numbers, Collections, Maps
and various other data structures easier to use.
Various operators in Groovy are mapped onto regular Java method calls on objects. This allows you the developer to provide your own Java or Groovy objects which can take advantage of operator overloading. The following table describes the operators supported in Groovy and the methods they map to.
Operator Method a + b a.plus(b) a - b a.minus(b) a * b a.multiply(b) a ** b a.power(b) a / b a.div(b) a % b a.mod(b) a | b a.or(b) a & b a.and(b) a ^ b a.xor(b) a++ or ++a a.next() a-- or --a a.previous() a[b] a.getAt(b) a[b] = c a.putAt(b, c) a << b a.leftShift(b) a >> b a.rightShift(b) switch(a) { case(b) : } b.isCase(a) ~a a.bitwiseNegate() -a a.negative() +a a.positive()
Note that all the following comparison operators handle nulls gracefully avoiding the throwing of java.lang.NullPointerException
Operator Method a == b a.equals(b) or a.compareTo(b) == 0 ** a != b ! a.equals(b) a <=> b a.compareTo(b) a > b a.compareTo(b) > 0 a >= b a.compareTo(b) >= 0 a < b a.compareTo(b) < 0 a <= b a.compareTo(b) <= 0
Also in Groovy comparison operators handlenull
s gracefully. So thata == b
will never throw aNullPointerException
whethera
orb
or both are null.
groovy:000> a = null ===> null groovy:000> b = "foo" ===> foo groovy:000> a != b ===> true groovy:000> b != a ===> true groovy:000> a == null ===> true
In addition when comparing numbers of different types the type coercion rules apply to convert numbers to the largest numeric type before the comparison. So the following is valid in Groovy
~/src/groovy/overloading$ cat -n Numberscohercioncomparison.groovy 1 #!/usr/bin/env groovy 2 Byte a = 12 3 Double b = 256 4 5 println "a($a) instanceof Byte: ${a instanceof Byte}" 6 println "b($b) instanceof Double: ${b instanceof Double}" 7 8 if (b > a) { 9 println "(b > a): Cohercion working. $b is greater than $a" 10 println "($b > $a) instanceof Boolean: ${(b > a) instanceof Boolean}" 11 } 12 13 if (a > (Byte) b) { 14 println "Byte $a is greater than (byte) Double $b" 15 println "($a > (Byte) $b) instanceof Boolean: ${(a > (Byte) b) instanceof Boolean}" 16 }
Al ejecutar este ejemplo obtenemos la salida:
~/src/groovy/overloading$ ./Numberscohercioncomparison.groovy a(12) instanceof Byte: true b(256.0) instanceof Double: true (b > a): Cohercion working. 256.0 is greater than 12 (256.0 > 12) instanceof Boolean: true Byte 12 is greater than (byte) Double 256.0 (12 > (Byte) 256.0) instanceof Boolean: true
Theequals()
method ofjava.lang.Object
acts the same as the==
operator; that is, it tests for object identity rather than object equality. The implicit contract2.2 of theequals()
method, however, is that it tests for equality rather than identity. Thus most classes will overrideequals()
with a version that does field by field comparisons before deciding whether to returntrue
orfalse
.
En Groovy:
The==
operator doesn't always exactly match the.equals()
method. You can think of them as equivalent in most situations. In situations where two objects might be thought equal via normal Groovy coercion mechanisms, the==
operator will report them as equal; the.equals()
method will not do so if doing so would break the normal rules Java has around the equals method. Expect further improvements to Groovy over time to provide clearer, more powerful and more consistent behavior in this area.
Sobreescribir isCase
altera la conducta del operador in
.
El siguiente programa:
generaciondecodigos@nereida:~/Lgroovy/overloading$ cat -n isCase1.groovy 1 class A{ 2 boolean isCase(Object o){ 3 if(o == 'A') return true 4 else return false 5 } 6 } 7 8 def a= new A() 9 10 println "a.isCase('A') = "+a.isCase('A') 11 println "('A' in a) = "+('A' in a) //more common, shortcut syntax for isCase() 12 13 println "(a.isCase('Z') = "+ (a.isCase('Z')) 14 println "('Z' in a) = "+ ('Z' in a) //more common, shortcut syntax for isCase()
Produce la salida:
generaciondecodigos@nereida:~/Lgroovy/overloading$ groovy isCase1.groovy a.isCase('A') = true ('A' in a) = true (a.isCase('Z') = false ('Z' in a) = false
He aqui otro ejemplo:
generaciondecodigos@nereida:~/Lgroovy/overloading$ cat -n isCase2.groovy 1 class MyList extends ArrayList { 2 boolean isCase(Object val) { 3 return val == 66 4 } 5 } 6 7 def myList = new MyList() 8 myList << 55 9 println (55 in myList) // return false but myList.contains(55) returns true 10 println (66 in myList) // returns true but myList.contains(55) returns false
The switch
statement inspects an expression and resumes execution from
the first matching case-expression, ie, regex matched, list or set or
range contained in, class an instance of, or object equal to.
Veamos un ejemplo:
generaciondecodigos@nereida:~/Lgroovy/overloading$ cat -n Switch1.groovy 1 def values= [ 2 'abc': 'abc', 3 'xyz': 'list', 4 18: 'range', 5 31: BigInteger, 6 'dream': 'something beginning with dr', 7 1.23: 'none', 8 ] 9 values.each{ 10 def result 11 switch( it.key ){ 12 case 'abc': //if switched expression matches case-expression, execute all 13 //statements until 'break' 14 result= 'abc' 15 break 16 case [4, 5, 6, 'xyz']: 17 result= 'list' 18 break 19 case 'xyz': //this case is never chosen because 'xyz' is matched by 20 //previous case, then 'break' executed 21 result= 'xyz' 22 break 23 case 12..30: 24 result= 'range' 25 break 26 case Integer: 27 result= Integer //because this case doesn't have a 'break', result 28 //overwritten by BigInteger in next line 29 case BigInteger: 30 result= BigInteger 31 break 32 case ~/dr.*/: 33 result= 'something beginning with dr' 34 break 35 case {it instanceof Integer && it>30}: //use Closure 36 result= 'result is > 30' 37 break 38 default: 39 result= 'none' 40 } 41 println "it = (${it.key}, ${it.value}) => result == $result" 42 }Este es el resultado de la ejecución:
generaciondecodigos@nereida:~/Lgroovy/overloading$ groovy Switch1.groovy it = (abc, abc) => result == abc it = (xyz, list) => result == list it = (18, range) => result == range it = (31, class java.math.BigInteger) => result == class java.math.BigInteger it = (dream, something beginning with dr) => result == something beginning with dr it = (1.23, none) => result == none
When we supply our own values in the case-expression, theisCase
method is invoked to determine whether or not the switch-expression is matched. If there's noisCase
method, theequals
method is used to test for equality:
generaciondecodigos@nereida:~/Lgroovy/overloading$ cat -n isCase3.groovy 1 class A{ 2 boolean isCase(Object switchValue){ //'isCase' method used for case-expression 3 if(switchValue == 'Hi') return true 4 else return false 5 } 6 } 7 switch( 'Hi' ){ 8 case new A(): 9 println "case A" 10 break 11 default: 12 println "A was false" 13 } 14 15 class B{ 16 boolean equals(Object switchValue){ //'equals' method used for case-expression 17 this.class == switchValue.getClass() 18 } 19 } 20 switch( new B() ){ 21 case new B(): 22 println "case B" 23 break 24 default: 25 println "B was false" 26 }El resultado de la ejecución del programa anterior es:
generaciondecodigos@nereida:~/Lgroovy/overloading$ groovy isCase3.groovy case A case B
"because a Javaswitch/case
does not work like a Groovyswitch/case
. In Java acase
can take onlyint
compatible constants, in Groovy it can take expressions. In Java all cases share a scope, in Groovy eachcase
has its own scope. In Groovy we call theisCase
method, in Java it has to be anumber
we switch with. If we for example use a closure as case, then this might cause side effects. There are cases where we can let them behave the same and usually when using the java version you won't see a difference in Groovy besides the placement and logic of default."
So, while in Java thedefault
can be placed anywhere in theswitch/case
, the default in Groovy is used more as anelse
than assigning adefault
case.
El siguiente ejemplo muestra implementaciones de los operadores de igualdad equals
y
suma plus
para una clase Money
.
generaciondecodigos@nereida:~/src/groovy/overloading$ cat -n Operator_overrride.groovy 1 #!/usr/bin/env groovy 2 class Money { 3 private int amount 4 private String currency 5 6 Money(amountValue, currencyValue) { 7 amount = amountValue 8 currency = currencyValue 9 } 10 11 boolean equals (Object other) { 12 if (null == other) return false 13 if (!(other instanceof Money)) return false 14 if (currency != other.currency) return false 15 if (amount != other.amount) return false 16 17 return true 18 } 19 20 int hashCode() { 21 amount.hashCode()+currency.hashCode() 22 } 23 24 Money plus(Money other) { 25 if (other == null) return null 26 if (other.currency != currency) 27 throw new IllegalArgumentException( 28 "cannot add $other.currency to $currency" 29 ) 30 return new Money(amount+other.amount, currency) 31 } 32 33 String toString() { 34 "$amount $currency" 35 } 36 } 37 38 39 def buck = new Money(1, '$') 40 println buck+buck 41 42 def euro = new Money(1, '€') 43 println euro+euro 44 45 assert(buck == new Money(1, '$'))
equals
imita el estilo Groovy, evitando
que se produzca una java.lang.NullPointerException.
Recuérdese que ==
denota igualdad entre objetos/valores y no identidad.
instanceof Money
retornamos false
:
if (!(other instanceof Money)) return falseUna alternativa hubiera sido usar double-dispatch2.3 y hacer que el otro operando nos promocione, delegando en su método
equals
.
Quizá la otra clase conoce como compararse con
objetos de la clase Money
.
if (!(other instanceof Money)) { other.equals(this) }
hashCode
de la clase Object.
83 int hashCode() { [real, imag].hashCode() }
Esto forma parte del contrato
que Java impone en la función hashCode
cuando se sobrecarga equals
:
public int hashCode()
Returns a hash code2.4 value for the object. This method is supported for the benefit of hashtables such as those provided by java.util.Hashtable.
The general contract of hashCode
is:
hashCode
method must consistently
return the same integer, provided no information used in equals
comparisons on the object is modified. This integer need not remain
consistent from one execution of an application to another execution of
the same application.
equals
(Object)
method, then calling the hashCode
method on each of the two objects must
produce the same integer result.
equals
(Object) method, then calling the hashCode
method
on each of the two objects must produce distinct integer results. However,
the programmer should be aware that producing distinct integer results
for unequal objects may improve the performance of hashtables.
hashCode
method defined by
class Object does return distinct integers for distinct objects. (This
is typically implemented by converting the internal address of the
object into an integer, but this implementation technique is not
required by the Java programming language.)
plus
:
24 Money plus(Money other) { 25 if (other == null) return null 26 if (other.currency != currency) 27 throw new IllegalArgumentException( 28 "cannot add $other.currency to $currency" 29 ) 30 return new Money(amount+other.amount, currency) 31 } 32
Es necesario asegurarse que los argumentos tienen
el tipo apropiado para llevar a cabo la operación.
En el ejemplo se ha optado por lanzar una excepción IllegalArgumentException
cuando el otro objeto Money
no es de la misma divisa.
No es del todo correcto decir que hay sobrecarga del plus
en cuanto que no existe tal operador en la superclase Object.
Sería mas preciso hablar de implementación del operador.
Si que tendría sentido usar el término sobrecarga si añadimos un método para sumar
objetos Money
con objetos Integer
:
Money plus(Integer other) { return new Money(amount+other, currency) }el mecanismo de dispatch de Groovy encontrará la implementación correcta en tiempo de ejecución.
toString()
.
33 String toString() { 34 "$amount $currency" 35 }
When Java converts data into its string representation during concatenation, it does so by calling one of the overloaded versions of the string conversion methodvalueOf()
defined byString
.
valueOf()
is
overloaded for all the simple types and for type Object.
valueOf()
returns a string that contains the human-readable
equivalent of the value with which it is called.
valueOf()
calls the toString()
method on the object.
The toString()
method is the means by which you can determine the string
representation for objects of classes that you create.
Every class implementstoString()
because it is defined by Object. However, the default implementation oftoString()
is seldom sufficient. For most important classes that you create, you will want to overridetoString()
and provide your own string representations.
By overridingtoString()
for classes that you create, you allow the resulting strings to be fully integrated into Java's programming environment. For example, they can be used inprint()
andprintln()
statements and in concatenation expressions.