Multimethods
Based on section 3.6 "Multimethods" in "Programming Groovy 2" by Venkat Subramaniam.
Run the following codes and see the differences between Java and Groovy programs:
$ cat << EOF > Employee.java
public class Employee {
public void raise(Number amount) {
System.out.println("Employee got raise");
}
}
EOF
$ cat << EOF > Executive.java
public class Executive extends Employee {
public void raise(Number amount) {
System.out.println("Executive got raise");
}
public void raise(java.math.BigDecimal amount) {
System.out.println("Executive got outlandish raise");
}
}
EOF
$ cat << EOF > GiveRaise.java
import java.math.BigDecimal;
public class GiveRaise {
public static void giveRaise(Employee employee) {
employee.raise(new BigDecimal(10000.00));
}
public static void main(String[] args) {
giveRaise(new Employee());
giveRaise(new Executive());
}
}
EOF
$ javac GiveRaise.java
$ java GiveRaise
Employee got raise
Executive got raise
$ cat << EOF > GiveRaise.groovy
void giveRaise(Employee employee) {
employee.raise(new BigDecimal(10000.00))
}
giveRaise new Employee()
giveRaise new Executive()
EOF
$ groovy GiveRaise.groovy
Employee got raise
Executive got outlandish raise
The behavior of employee.raise(new BigDecimal(10000.00));
at rum time
is different in Java and Groovy.
For Java, whether raise(Number)
or raise(BigDecimal)
is decided at
compile time, only by the type of callee employee
(Employee
).
For Groovy, which method used is decided at run time,
by both the type of employee
and the parameter of the method raise
(new BigDecimal(10000.00)
).
This is called multiple dispatch or multimethods.
Multi-type data collection
Based on 10 things your static language can’t do.
For both section "Multimethods" and "Duck typing" in origin article.
$ cat << EOF > MultiMethod.java
import java.util.Date;
public class MultiMethod {
public int foo(String p) { return 1; }
public int foo(Date p) { return 2; }
public int foo(Object p) { return 3; }
public static void main(String[] args) {
MultiMethod mm = new MultiMethod();
Object[] array = new Object[] {"a string", new Date(), 666};
for (Object o : array) {
System.out.println(mm.foo(o));
}
}
}
EOF
$ javac -version
javac 1.8.0_144
$ javac MultiMethod.java
$ java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)
$ java MultiMethod
3
3
3
$ cat << EOF > MultiMethod.java
import java.util.Date;
public class MultiMethod {
public int foo(String p) { return 1; }
public int foo(Date p) { return 2; }
public static void main(String[] args) {
MultiMethod mm = new MultiMethod();
Object[] array = new Object[] {"a string", new Date(), 666};
for (Object o : array) {
if (o instanceof Date) {
System.out.println(mm.foo((Date)o));
} else if (o instanceof String) {
System.out.println(mm.foo((String)o));
}
}
}
}
EOF
$ javac MultiMethod.java
$ java MultiMethod
1
2
$ cat << EOF > MultiMethod.groovy
int foo(String p) { 1 }
int foo(Date p) { 2 }
int foo(Object p) { 3 }
def array = ["a string", new Date(), 666]
array.each { println(foo(it)) }
EOF
$ groovy Multimethods.groovy
1
2
3
So static typing is much more verbose and limited in the scenario involved with multi-type collection.
Respond to non-existing methods
The correspondent implementation of ActiveRecord in Ruby is method_missing.
Summary
Dynamic languages are good tools for rapid prototying for using fewer codes to implement the same function, and more flexible in data structure. They are more good at building DSL and metaprogramming (Clojure, Ruby, etc).
On the other side dynamic languages can't guarantee type safe (while static codes seems can't too). And it is slower (but in most case hardware promotion can make up for this).
Maybe developers should use dynamic languages building prototypes of the product, then rewrite with static languages to reduce the runtime errors and potential bugs.