(注:这篇文章有点难懂,所以我直接翻译了大部分原文...标题:SubtypePolymorphismvs.ExplicitHigherOrderFunctions)1.温馨up:动态方法选择假设我们有两个类,Dog和ShowDog,其中showDog实现了Dog,并且都有bark()方法,showDog重写bark()方法总结了“is-a”关系,我们可以得到:所有showDogs都是DogsandeveryDogisObjectJava中的所有类型都是Object的子类型现在,考虑以下代码:Objecto2=newShowDog("Mortimer","Corgi",25,512.2);ShowDogsdx=((ShowDog)o2);sdx.bark();Dogdx=((Dog)o2);dx.bark();((Dog)o2).bark();Objecto3=(Dog)o2;o3.bark();对于每一行赋值,都要考虑是否会导致编译错误。每次调用bark()时,考虑调用ShowDog.bark()?或狗。吠()?还是语法错误查看规则:编译器允许内存框存储任何子类型的变量编译器检查一个方法是否可以调用取决于变量的静态类型动态方法选择只对子类有效方法,并且发生在运行时(runtime),此时对方法的调用取决于变量的动态类型。casting(强制类型转换)长期无效,只作用于瞬间使用cast类型转换的特定表达式(相当于这行代码有效,下一行无效,变量恢复为其原始类型),相当于欺骗编译器:“相信我,这个变量是这个类型”,从而绕过编译器的类型检查,本质上强制类型转换不会改变隐藏变量指向的真实类型是一种糟糕的风格。例如,子类中的变量与父类中的变量同名。子类中的静态方法与父类中的静态方法声明一致(包括参数名)。对于静态方法,我们不叫它Override,我们叫它隐藏在上面,josh不叫不会在61B中教授它,因为他认为这是一个语法错误,如果您有兴趣,请参阅此链接2。SubtypePolymorphism多态性:为不同类型的实体提供单一接口在Java中,多态性意味着一个对象可以有多种形式或类型。在面向对象的编程中,多态性涉及如何将对象视为其自身类的实例、其超类的实例以及其超类的超类的实例。etc.SubtypePolymorphism:UpcastingandlatebindingUpcasting:即子类可以转化为超类,比如Shapes=newCircle(),因为Circle是Shape的子类,编译器运行Shape的内存盒来容纳Circle,从而实现upcastinglateBinding:假设Shape声明了一个draw()方法,它的Circle子类重写了这个方法,Shapes=newCircle();下一行执行s.draw();调用了哪个draw()方法?Shape的draw()方法还是Circle的draw()方法?编译器不知道调用哪个draw()方法,它所能做的就是验证超类中是否存在该方法,并验证方法调用的参数列表和返回类型是否与超类的方法声明匹配,这是在早期阶段,根据静态类型进行检查。但是,编译器也会在编译后的代码中插入一条指令,然后根据DynamicmethodAlgorithm选择具体的执行方式。此任务称为后期绑定。3.DIY比较假设我们现在需要写一个程序,其功能是在python中打印出两个Object中较大的一个,1.使用显式调用高阶函数的方法写:defprint_larger(x,y,compare,stringify):ifcompare(x,y):returnstringify(x)returnstringify(y)compare()有时也称为回调(callback)2.使用子类型多态的方法写:defprint_larger(x,y):ifx.largerThan(y):returnx.str()returny.str()使用高阶函数显式调用方式,可以用普通的方式打印出结果,相对于在子类型多态的方法中,对象对largeFunction()的调用取决于x和y实际是什么。假设我们要写一个max()可以返回任何类型数组中的最大值。红框内的元素在代码中,哪一行会编译错误?很明显items[i]>items[maxDex]因为在Java中,Object不能用>来相互比较。另外,Java不支持运算符重载,这也意味着Java不能像C++和python那样重载>一个原生的解决方案是在Dog类中定义max()方法:publicstaticDogmaxDog(Dog[]dogs){if(dogs==null||dogs.length==0){返回null;}狗maxDog=dogs[0];for(Dogd:dogs){if(d.size>maxDog.size){maxDog=d;}}returnmaxDog;}然后你可以调用:Dog[]dogs=newDog[]{d1,d2,d3};最大的狗=Dog.maxDog(dogs);但是这样做的缺点是,如果当前Object不是Dog怎么办?这是猫,鱼,还是狮子?难道我们不必为每个Animal类编写一个max()吗?我们希望能够创建一个适用于一般类型的max(),它可以比较任何Animals。所以,Solution就是创建一个接口,在里面声明一个比较方法,创建一个Dog类,implementsinterfacepublicinterfaceOurComparable{publicintcompareTo(Objecto);//参数Object也可以改成OurComparable,没有difference}对于compareTo()函数,定义它的返回值:如果当前对象thisobjecto,返回正数我们使用大小作为比较的基准publicclassDogimplementsOurComparable{privateStringname;私有整数大小;publicDog(Stringn,ints){名字=n;大小=小号;}publicvoidbark(){System.out.println(name+"says:bark");}publicintcompareTo(Objecto){DoguddaDog=(Dog)o;返回this.size-uddaDog.size;}}注意,因为compareTo(Objecto)是一个任意的Objecto,所以我们需要将Object类型o转换为Dog类型(使用uddaDog来存储o),以保证可以使用size变量进行比较,否则有对象类中没有尺寸完成上述工作后,我们可以泛化max()方法,它将适用于任何类型的动物,而不仅仅是狗:publicclassMaximizer{publicstaticOurComparablemax(OurComparablemax(OurComparable[]items){intmaxDex=0;//OurComparable也可以改成Objectfor(inti=0;i0){maxDex=i;}}returnitems[maxDex];}}Dog上的Max():Dog[]dogs=newDog[]{d1,d2,d3};Doglargest=(Dog)Maximizer.max(dogs);另外,如果我们要比较Cat类,我们创建一个Cat类来实现我们的Comparable接口,所以此时的max()适用于所有Animals4.InterfacesQuizQ1:如果我们忽略Dog类中的compareTo(),这文件会编译错误?A:Dog.java会编译错误,因为Dog声明了implementsOurComparable接口,也就是说Dog类承诺实现了OurComparable声明的所有method(),当Dog类不包含这些方法时,会报错error而DogLauncher.java也会报错,因为Dog.java的编译错误导致javac无法生成Dog.class文件,然后使用Dog。在DogLaucher.java中会报错Q2:如果忽略Dog类头中的implementsOurComparable,下面哪个文件会报错?A:DogLauncher.java会报错,因为当Dog类没有声明implements时,Dog类不属于OurComparable的子类,OurComparable的内存盒不能容纳Dog类型,所以在尝试Maximiz时当er.max()传入一个Dog类型的狗数组作为参数时,会报错5.Comparables接口我们已经完成了OurComparable接口,但是还有很多不完善的地方,例如:每次都要对象之间的强制类型转换:DoguddaDog=(Dog)o;Dog[]dogs=newDog[]{d1,d2,d3};最大的狗=(Dog)Maximizer.max(dogs);没有现有的类实现OurComparable(例如String等)没有现有的类使用OurComparable(例如没有使用OurComparable的内置max函数)Java有一个更完整和强大的内置接口,类似于我们的实现OurComparable,并且支持泛型使用:只需将T替换为6。Comparator考虑Java是如何实现函数回调(callbacks)的。python中,使用高阶函数显示回调,将compare作为参数传给defprint_larger(x,y,compare,stringify):ifcompare(x,y):returnstringify(x)returnstringify(y)如何以子类型多态方式实现回调?defprint_larger(Tx,Ty):ifx.largerThan(y):returnx.str()returny.str()添加一个参数comparatorc这个参数可以让我们自定义比较规则,即相当于C++STLSort()的cmp参数,如:比较大小或比较name的字典顺序defprint_larger(Tx,Ty,comparatorc):ifc.compare(x,y):returnx.str()returny.str()是Java中我们要介绍的另一个内置接口Comparator,我们上面比较的是两条狗的大小。如果我们按字母顺序比较他们的名字呢?您可以使用Comparator,因为Comparator是一个对象。我们使用Comparator的方式是在Dog类中嵌套另一个类来实现Comparator接口publicinterfaceComparator{intcompare(To1,To2);}规则类似于compareTo():返回负数如果o1o2.返回正数.importjava.util.Comparator;公共类DogimplementsComparable{...publicintcompareTo(DoguddaDog){returnthis.size-uddaDog.size;}publicstaticclassNameComparatorimplementsComparator{publicintcompare(Doga,Dogb){returna.name.compareTo(b.name);}}}由于嵌套类NameComparator没有使用外层类Dog的任何成员变量(实例变量),可以改为static来优化内存空间。DogLauncher.java中的调用形式为:Dog.NameComparatornc=newDog.NameComparator();但是现代的Java代码风格并不是这样调用的。我们考虑封装类NameComparator,修改为private:privatestaticclassNameComparatorimplementsComparatorgetNameComparator(){returnnewNameComparator();所以在DogLauncher中调用.java:Comparatorcd=newDog.NameComparator();if(cd.compare(d1,d3)>0){d1.bark();}else{d3.bark();}function是一样的,后者为我们提供了进行现代代码风格的函数回调的能力接口。有时一个函数需要另一个可能尚未编写的函数的帮助。例如:max需要compareTo这个辅助函数有时被称为“回调”。有些语言使用显式函数传递来处理这个,egpython,javascript。在Java中,我们通过将需要的函数封装在一个接口中来实现(例如,Arrays.sort需要比较,它在比较器接口中)。Arrays.sort在需要比较时“回调”。类似于在需要信息时将您的电话号码提供给某人。