将自己读入Lisp,目前在此页面上{http://landoflisp.com),我在点击链接 CLOS GUILD 的页面上的第二个最后一段发现了以下声明:< / p>
关于该示例需要注意的重要事项是,为了确定在给定情况下调用哪种混合方法,CLOS需要考虑传递给该方法的两个对象。它基于多个对象的类型调度到方法的特定实现。这是传统的面向对象语言(如Java或C ++)中不可用的功能。
以下是Lisp代码示例:
(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!")
不,我认为最后一句话是错的。实际上,我可以使用以下Java代码完成此操作:
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 {}
当我运行它时,给了我相同的输出:
I don't know what color that makes
you made orange!
所以我的问题是:该页面上的这句话实际上是错误的,并且在Java / C ++中是可能的吗? 如果是这样,也许在旧版本的Java中不可能? (虽然我非常怀疑,因为这本书只有5岁) 如果不是这样,在我的例子中我忘记了什么?
答案 0 :(得分:9)
如果您将示例更改为:
public static void main(String[] args) {
Color red = new Red();
Color blue = new Blue();
Color yellow = new Yellow();
mix(red, blue);
mix(yellow, red);
}
然后你会得到
I don't know what color that makes
I don't know what color that makes
Java正在使用编译时类型来确定要调用的方法的重载,其中Lisp在运行时类型上进行调度。 Java必须使用Visitor模式才能进行双重调度(根据调用该方法的对象类型和传入的参数类型调用不同的实现)。
答案 1 :(得分:4)
C ++和Java都可以通过多个参数重载方法,但它是静态完成的,只适用于方法(对于Java,它们是类和对象下的二等公民)。它本身并没有解决表达式问题:你不能引入一个新的类,它带有一组支持的参数组合,并让它与现有的一起工作。
您可以利用JVM支持的反射机制,并编写一个&#34;注册&#34;方法由它们的参数组成,并将调度数据存储到该类的存储中。有助于推断出参数类型的反射不是在JVM中分派的快速方法,但可能需要实现泛型函数。
答案 2 :(得分:2)
你在Java中做OO的实际方式更像是:
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);
}
}
现在,如果您想象Yellow
由您制作,但其余部分由其他人制作。在CLOS中,您可以提供(defmethod mix ((c2 red) (c1 yellow))
作为yellow
实施的一部分,但是如何在public void mix(Color c)
中提供重载class Blue
以返回“你做了橙色!”没有修改它?
答案 3 :(得分:2)
您将从以下示例中获得更好的图片:
class PrettyPrint{
public void Print(Customer aCustomer){}
public void Print(User aUser){}
public void Print(SuperUser aUser){}
}
看起来像方法重载。但是有区别: 如果你这样做(假设&#34; Person&#34;是Customer,User和SuperUser的超类):
PrettyPrint pp = ...;
Person p = new Customer(...);
pp.Print(p);
,然后C#编译器会抱怨,因为它不知道它应该调用哪三种方法。 如果C#有多种方法,这将按预期编译和运行,即第一个&#34;打印&#34;将被调用。这是因为,使用多方法时,所有参数的动态(运行时)类型(不仅仅是第一个)在运行时被认为是 打电话的方法。
multimethod调用使用参数的基类型,但仍然可以找到派生类型的相应实现。
我猜您应该从以下链接中阅读以获得更好的图片:
答案 4 :(得分:1)
这里的不同之处在于,在Java中,对于一个参数的调度,您将使用常规类方法(在类文件中),并且对于许多参数的调度,您将使用辅助类(在其自己的文件中),就像在您的示例中一样。在CLOS中它是统一的,你可以使用一个表单将所有内容分成适合你的文件。