下面这段 Java 多态的代码,如何才能改为 Go、 Python 、JS、C++版本? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
jarryli
V2EX    Java

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

  •  
  •   jarryli 1 小时 47 分钟前 313 次点击

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

    但是 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

    7 条回复    2026-03-18 20:47:16 +08:00
    YanSeven
        1
    YanSeven  
       1 小时 34 分钟前
    什么叫类似的效果。
    jarryli
        2
    jarryli  
    OP
       1 小时 17 分钟前
    @YanSeven 就是完整模拟 java 多态的效果。
    dcsuibian
        3
    dcsuibian  
       53 分钟前
    不了解 Go 和 C++,但是 Javascript/TypeScript 和 Python 和 Java 我还是了解的。
    我说直白点,你在其它语言里追求另一种语言的写法是白费工夫

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


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

    或者说 Java 味太重了。即使你真的实现了,在 js 和 python 中大家也不那么写,没有用
    pursuer
        4
    pursuer  
       52 分钟前
    毕竟通用编程语言都是图灵完备的,通常要实现什么逻辑都是可以的,只是有的麻烦有的简单。

    除了 JS 和 Python 这样的脚本语言自带的 eval ,其他语言通常较少具备对等特性。
    liuliuliuliu
        5
    liuliuliuliu  
    PRO
       41 分钟前
    要不试试 C# ?
    不过第 9 条行为会不同,C# 的方法重载解析规则是“一旦在派生类(B)中找到了适用的方法,就不会再去基类(A)中搜索”。在 C# 中,因为 D 继承自 B ,所以参数 d 适用 B.Show(B)。既然在 B 中找到了,编译器直接忽略基类 A 里的 Show(D)。

    jarryli
        6
    jarryli  
    OP
       36 分钟前
    @dcsuibian 谢谢。其实我想通过多态来研究语言特性。经验实践 JS Go Py 等均无法实现或者翻译 Java 这种效果。这正是不同语言设计哲学所带来的特性,也很有趣。
    rb6221
        7
    rb6221  
       7 分钟前
    为什么要模拟
    你举得几个语言都恰好不是 OOP ,或者不完全 OOP 的语言,我都怀疑你是故意立靶子打。
    每个语言有自己的特性和使用场景,用到最合适处就没问题了,非要用在不合适处,那我也没话说
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3267 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 12:54 PVG 20:54 LAX 05:54 JFK 08:54
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86