GoForum🌐 V2EX

下面这段 Java 多态的代码,如何才能改为 Go、 Python 、JS、C++版本?

jarryli · 2026-03-18 19:12 · 0 次点赞 · 6 条回复

面向对象包括封装、继承、组合、多态,其中最不好理解,也最强悍的就是多态。可以说掌握了多态就掌握了面向对象以及设计模式的精髓。

但是 java 里面的多态实现太过于严苛,以致于其他语言都无法完全模拟。请看下面的代码,用其他语言实现的时候总难以达到类似效果。

/*
多态:即同一个行为具有多个不同表现形式或形态的能力。
表现形式为,子类重写父类方法,实现类实现接口方法,子类重写抽象类方法等。
多态三个必要条件:继承、重写、父类引用指向子类对象。多态有效消除类型之间的耦合,并提供灵活的可扩展方案。
本例子简单清晰明了的 Java 多态,能看懂这个例子就懂了什么是多态。
*/

// 父类 A
class A {
    public String show(D object) {
        return ("A and D");
    }

    public String show(A object) {
        return ("A and A");
    }

    // 默认注释掉。可开关注释测试下
    // public String show(B object) {
    // return ("A and B");
    // }
}

// 子类 B
class B extends A {
    public String show(B object) {
        return ("B and B");
    }

    public String show(A object) {
        return ("B and A");
    }
}

// 孙子类 C
class C extends B {
}

// 孙子类 D
class D extends B {
}

// 测试验证
public class PolymorphismSimple {
    public static void main(String[] args) {
        // 父类声明自己
        A a = new A();
        // 父类声明子类
        A ab = new B();
        // 子类声明自己
        B b = new B();
        C c = new C();
        D d = new D();

        // 1) A and A 。b 的类型是 B ,也是 B 的实例,A 里没有 show(B)方法,但有 show(A)方法。B 的父类是 A ,因此定位到 A.show(A)。
        System.out.println("1) " + a.show(b));

        // 2) A and A 。c 的类型是 C ,也是 C 的实例,C 继承 B ,B 继承 A 。A 里没有 show(C)方法,也没有 show(B)方法,最后指向 A.show(A)。
        System.out.println("2) " + a.show(c));

        // 3) A and D, d 的类型是 D ,也是 D 的实例,D 继承 B ,B 继承 A 。A 里有 show(D)方法,直接定位到 A.show(D)。
        System.out.println("3) " + a.show(d));

        // 4) B and A, ab 是 B 的实例,但用 A 声明,即向上转型得到的类型是 A ,运行时才能确定具体该调用哪个方法。
        // ab 是 B 的实例对象,但引用类型是 A 。类型是在编译时确定,因此从类型开始定位方法。
        // A 类中没有 show(B)方法,但有 show(A)方法,因为 A 是 B 的父类,ab 也是 A 的实例,于是定位到 A.show(A)方法。
        // 由于 B 是 A 的子类,且 B 重写了 A 的 show(A),A 的方法被覆盖了,于是定位到 B.show(A),这就是动态绑定。
        // 虽然 B 中有 show(B)方法,但是因为 ab 的类型是 A ,编译时根据类型定位到 A 的方法,而不是 B 。

        // 以下几种可开关打开/注释代码测试下。
        // -
        // 若 A 里有 show(A)和 show(B),B 里有 show(B)有 show(A),则编译时关联到 A.show(B),因 B 覆盖了 A.show(B),动态绑定到 B.show(B)。
        // -
        // 若 A 里有 show(A)和 show(B),B 里无 show(B)有 show(A),则编译时关联到 A.show(B),因 B 无覆盖,则直接调用 A.show(B)。
        // -
        // 若 A 里有 show(A)无 show(B),B 里无 show(B)有 show(A),则编译时关联到 A.show(A),因 B 覆盖了 A.show(A),动态绑定到 B.show(A)。
        // -
        // 若 A 里有 show(A)无 show(B),B 里无 show(A)有 show(B),则编译时关联到 A.show(A),因 B 无覆盖,则直接调用 A.show(A)。
        // 查找顺序为:编译时根据引用类型确定所属类 -> 根据重载参数类型定位(类型按子->父->祖逐级往上查找)到类的具体方法(包括继承的方法) ->
        // 运行时实例对象覆盖(覆盖只有子->父一层)了引用类型的同名方法 -> 定位到实例对象的方法。
        System.out.println("4) " + ab.show(b));

        // 5) B and A 。ab 是 B 的实例,类型是 A 。从 A 类没找到 show(C)方法,也没找到 A.show(B)方法,找到 A.show(A)方法。A.show(A)被 B.show(A)覆盖,因此调用 B.show(A)。
        System.out.println("5) " + ab.show(c));

        // 6) A and D 。A 里面有 show(D)的方法,直接定位到。
        System.out.println("6) " + ab.show(d));

        // 7) B and B 。B 里面有 show(B)的方法,直接定位到。
        System.out.println("7) " + b.show(b));

        // 8) B and B 。B 没有 show(c)方法,但有 show(B)方法。C 继承自 B ,父类型是 B ,因此调用 B.show(B)。
        System.out.println("8) " + b.show(c));

        // 9) A and D 。B 中没有 show(D)方法,B 继承 A ,A 里有 show(D), 故调用 A.show(D)方法。
        System.out.println("9) " + b.show(d));

        // 10) B and A 。父类声明子类,存在向上转型。A 里有 show(A),被 B.show(A)覆盖了,因此定位到 B.show(A)。
        System.out.println("10) " + ab.show(a));

    }
}

/**
 * 测试结果
 * 1) A and A
 * 2) A and A
 * 3) A and D
 * 4) B and A
 * 5) B and A
 * 6) A and D
 * 7) B and B
 * 8) B and B
 * 9) A and D
 * 10) B and A
 */

其他语言模拟实现: https://github.com/microwind/design-patterns/tree/main/programming-paradigm/object-oriented-programming/polymorphism

6 条回复
YanSeven · 2026-03-18 19:22
#1

什么叫类似的效果。

jarryli · 2026-03-18 19:42
#2

@YanSeven 就是完整模拟 java 多态的效果。

dcsuibian · 2026-03-18 20:02
#3

不了解 Go 和 C++,但是 JavaScript/TypeScript 和 Python 和 Java 我还是了解的。 我说直白点,你在其它语言里追求另一种语言的写法是白费工夫

你这个项目的介绍中提到“研究设计模式”,但是《设计模式》书中说过: [img]https://i.imgur.com/LVFUk8y.png[/img] 程序设计语言的选择非常重要,它将影响人们理解问题的出发点。我们的设计模式采用了 Smalltalk 和 C++层的语言特性,这个选择实际上决定了哪些机制可以方便地实现,哪些则不能。如果采用过程式语言,那么可能就要包括诸如“继承”“封装”和“多态”的设计模式。相应地,一些特殊的面向对象语言可以直接支持我们的某些模式,例如,CLOS 支持多方法概念,这就减少了访问者等模式的必要性。

对于你的这个问题,如果你追求的是核心目标:“同一个调用,根据对象的不同产生不同的行为,可以方便地替换实现方式”,那么我可以跟你说 Python 和 JS 是咋做的 但是你问怎么实现“多态这个效果”,那你追求的不是这个核心目标,而是想复刻 Java 的实现路径

或者说 Java 味太重了。即使你真的实现了,在 js 和 python 中大家也不那么写,没有用

pursuer · 2026-03-18 20:02
#4

毕竟通用编程语言都是图灵完备的,通常要实现什么逻辑都是可以的,只是有的麻烦有的简单。

除了 JS 和 Python 这样的脚本语言自带的 eval ,其他语言通常较少具备对等特性。

liuliuliuliu · 2026-03-18 20:17
#5

要不试试 C# ? 不过第 9 条行为会不同,C# 的方法重载解析规则是“一旦在派生类(B)中找到了适用的方法,就不会再去基类(A)中搜索”。在 C# 中,因为 D 继承自 B ,所以参数 d 适用 B.Show(B)。既然在 B 中找到了,编译器直接忽略基类 A 里的 Show(D)。

https://i.imgur.com/mDiwk6F.png

jarryli · 2026-03-18 20:22
#6

@dcsuibian 谢谢。其实我想通过多态来研究语言特性。经验实践 JS Go Py 等均无法实现或者翻译 Java 这种效果。这正是不同语言设计哲学所带来的特性,也很有趣。

添加回复
你还需要 登录 后发表回复

登录后可发帖和回复

登录 注册
主题信息
作者: jarryli
发布: 2026-03-18
点赞: 0
回复: 0