Does Java support dispatching to specific implementations based on types of multiple objects like Lisp does?
Asked Answered
V

5

7

Reading myself into Lisp, currently on this page (http://landoflisp.com), I found the following statement on the second last paragraph on the page that shows when clicking the link CLOS GUILD:

The important thing to note about the example is that in order to figure out which mix method to call in a given situation, the CLOS needs to take into account both of the objects passed into the method. It is dispatching to a specific implementation of the method based on the types of multiple objects. This is a feature that is not available in traditional object-oriented languages, such as Java or C++.

Here is the example Lisp-code:

(defclass color () ())
(defclass red (color) ())
(defclass blue (color) ())
(defclass yellow (color) ())

(defmethod mix ((c1 color) (c2 color))
    "I don't know what color that makes")

(defmethod mix ((c1 blue) (c2 yellow))
    "you made green!")

(defmethod mix ((c1 yellow) (c2 red))
    "you made orange!")

No I think that the last sentence is wrong. I can actually do exactly that with the following Java code:

public class Main {
    public static void main(String[] args) {
        mix(new Red(), new Blue());
        mix(new Yellow(), new Red());
    }

    public static void mix(Color c1, Color c2) {
        System.out.println("I don't know what color that makes");
    }
    public static void mix(Blue c1, Yellow c2) {
        System.out.println("you made green!");
    }
    public static void mix(Yellow c1, Red c2) {
        System.out.println("you made orange!");
    }
}

class Color {}
class Red extends Color {}
class Blue extends Color {}
class Yellow extends Color {}

which gives me the same output, when I run it:

I don't know what color that makes
you made orange!

So my question is: Is this sentence on that page actually wrong and it is possible in Java / C++? If so, maybe it was not possible in an older version of Java? (Although I highly doubt that, since the book is only 5 years old) If not so, what did I forget to consider in my example?

Voroshilov answered 31/8, 2015 at 9:59 Comment(2)
#9759641Respectability
Java does not do dispatch in your example. It's statically determined by the compiler looking at the types. Common Lisp OTOH looks at the objects at runtime.Respectability
D
10

If you change your example to:

public static void main(String[] args) {
    Color red = new Red();
    Color blue = new Blue();
    Color yellow = new Yellow();
    mix(red, blue);
    mix(yellow, red);
}

then you will get

I don't know what color that makes
I don't know what color that makes

Java is using the compile time types to figure out which overload of the method to call, where Lisp is dispatching on the runtime types. Java has to use the Visitor pattern in order to do double dispatch (calling a different implementation based on both the type of the object the method is called on and the type of the argument passed in).

Drilling answered 31/8, 2015 at 13:2 Comment(0)
C
4

Both C++ and Java can overload methods by several arguments, but it's done statically and are just for methods (which, for Java, are second-class citizens under the classes and objects). It doesn't solve the expression problem per se: you can't introduce a new class with a new set of supported combinations of arguments and have it working with the existing ones.

You can utilize the reflection mechanism that JVM supports, and write a class that "registers" methods by their arguments, and store the dispatch data to a storage to that class. Reflection that helps to deduce the types of arguments is not the fast way to dispatch in JVM, but is probably needed to implement generic functions.

Caucus answered 31/8, 2015 at 10:18 Comment(0)
F
2

The actual way you would do OO in Java would be more like this:

class Color {
  public static void main(String[] args){
    Color red = new Red();
    Color blue = new Blue();
    Color yellow = new Yellow();
    red.printMix(blue);     // prints "I don't know what color that makes"
    red.printMix(yellow);   // prints "you made orange!"
    blue.printMix(yellow);  // prints "you made green!"
    // but these should work as well
    yellow.printMix(red);   // prints "you made orange!"
    yellow.printMix(blue);  // prints "you made green!"
  }
  public void printMix(Color c) {
    System.out.println(mix(c));
  }
  public String mix(Color c){
    return "I don't know what color that makes";
  }
}
class Red extends Color {
  @Override
  public String mix(Color c){
    if( c instanceof Yellow )
      return "you made orange!";
    return super.mix(c);
  }
}
class Blue extends Color {
  @Override
  public String mix(Color c){
    if( c instanceof Yellow )
      return "you made green!";
    return super.mix(c);
  }
}
class Yellow extends Color {
  @Override
  public String mix(Color c){
    if( c instanceof Red )
      return "you made orange!";
    if( c instanceof Blue )
      return "you made green!";
    return super.mix(c);
  }
}

Now if you imagine that Yellow is made by you but the rest is made by someone else. In CLOS you can supply (defmethod mix ((c2 red) (c1 yellow)) as a part of you implementation of yellow but how do you supply the overloading public void mix(Color c) in class Blue to return "you made orange!" without modifying it?

Fideicommissary answered 31/8, 2015 at 12:24 Comment(1)
All your examples print "I don't know what color that makes".Daman
D
2

You will get a better picture from the below example :

    class PrettyPrint{
       public void Print(Customer aCustomer){}
       public void Print(User aUser){}
       public void Print(SuperUser aUser){}
  }

It looks like method overloading.But there is a difference: If you do this (assuming "Person" is the superclass of Customer, User, and SuperUser):

 PrettyPrint pp = ...;
 Person p = new Customer(...);
 pp.Print(p);

, then the C# compiler will complain because it doesn't know which of the three methods it should call. If C# had multi-methods, this would compile and run as expected, i.e. the first "Print" would be called.This is because, with multimethods, the dynamic (run-time) types of all arguments (not just the first one) are considered at runtime when deciding which method to call.

The multimethod call uses the base type for the arguments, but still finds the appropriate implementations for the derived types.

I Guess you should read from the below link to get a better picture :

http://c2.com/cgi/wiki?MultiMethods

Direct answered 1/9, 2015 at 4:54 Comment(0)
V
1

The difference here is that in Java for dispatch on one argument you would use regular class methods (in the class file) and for dispatch on many arguments you would use a helper class (in its own file), like in your example. And in CLOS it is unified, you use one form for everything splitting into files as appropriate for you.

Vaporizer answered 31/8, 2015 at 10:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.