Java中的多态与继承

Java中的多态与继承Java 中的多态与继承本文学习 Java 中的多态及如何在多态方法调用中进行方法调用 多态 即对象根据其类型执行特定操作的能力 是 Java 代码灵活性的核心 四人组 Gang Of Four 创建的许多设计模式都依赖于某种形式的多态 包括命令

欢迎大家来到IT世界,在知识的湖畔探索吧!

Java中的多态与继承


本文学习Java中的多态及如何在多态方法调用中进行方法调用。

Java中的多态与继承

欢迎大家来到IT世界,在知识的湖畔探索吧!

多态——即对象根据其类型执行特定操作的能力——是Java代码灵活性的核心。四人组(Gang Of Four)创建的许多设计模式都依赖于某种形式的多态,包括命令模式。本文将介绍Java多态的基础知识及如何在程序中使用它。

关于Java多态需要了解的内容

  • 多态与Java继承
  • 为何多态重要
  • 方法重写中的多态
  • 核心Java类中的多态
  • 多态方法调用与类型转换
  • 保留关键字与多态
  • 多态的常见错误
  • 关于多态需要记住的要点

多态与Java继承

我们将重点探讨多态与Java继承的关系。需记住的核心点是:多态需要继承或接口实现。以下示例通过Duke和Juggy展示这一点:

public abstract class JavaMascot { public abstract void executeAction(); } public class Duke extends JavaMascot { @Override public void executeAction() { System.out.println("Punch!"); } } public class Juggy extends JavaMascot { @Override public void executeAction() { System.out.println("Fly!"); } } public class JavaMascotTest { public static void main(String... args) { JavaMascot dukeMascot = new Duke(); JavaMascot juggyMascot = new Juggy(); dukeMascot.executeAction(); juggyMascot.executeAction(); } }

欢迎大家来到IT世界,在知识的湖畔探索吧!

代码输出为:

欢迎大家来到IT世界,在知识的湖畔探索吧!Punch! Fly!

由于各自的具体实现,Duke和Juggy的动作均被执行。

为何多态重要

使用多态的目的是将客户端类与实现代码解耦。客户端类通过接收具体实现来执行所需操作,而非硬编码。这种方式下,客户端类仅需了解执行操作的必要信息,这是松耦合的典范。

为了更好地理解多态的优势,请观察以下SweetCreator:

public abstract class SweetProducer { public abstract void produceSweet(); } public class CakeProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cake produced"); } } public class ChocolateProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Chocolate produced"); } } public class CookieProducer extends SweetProducer { @Override public void produceSweet() { System.out.println("Cookie produced"); } } public class SweetCreator { private List<SweetProducer> sweetProducer; public SweetCreator(List<SweetProducer> sweetProducer) { this.sweetProducer = sweetProducer; } public void createSweets() { sweetProducer.forEach(sweet -> sweet.produceSweet()); } } public class SweetCreatorTest { public static void main(String... args) { SweetCreator sweetCreator = new SweetCreator( Arrays.asList( new CakeProducer(), new ChocolateProducer(), new CookieProducer() ) ); sweetCreator.createSweets(); } }

此例中,SweetCreator类仅知晓SweetProducer类,而不了解每个甜点的具体实现。这种分离使类能灵活更新和重用,并大幅提升代码可维护性。设计代码时,应始终寻求使其尽可能灵活和可维护。多态是编写可重用Java代码的强力技术。

提示:@Override注解强制程序员使用必须被重写的相同方法签名。若方法未被重写,将产生编译错误。

方法重载是多态吗?

许多程序员对多态与方法重写、重载的关系感到困惑。但只有方法重写是真正的多态。重载共享相同方法名但参数不同。多态是广义术语,因此相关讨论将持续存在。

方法重写中的多态

若返回类型是协变类型,则允许修改重写方法的返回类型。协变类型本质上是返回类型的子类。示例如下:

欢迎大家来到IT世界,在知识的湖畔探索吧!public abstract class JavaMascot { abstract JavaMascot getMascot(); } public class Duke extends JavaMascot { @Override Duke getMascot() { return new Duke(); } }

由于Duke是JavaMascot的子类,我们可在重写时修改返回类型。

核心Java类中的多态

我们在核心Java类中频繁使用多态。一个简单示例是实例化ArrayList类时声明List接口为类型:

List<String> list = new ArrayList<>();

进一步观察以下未使用多态的Java集合API代码:

欢迎大家来到IT世界,在知识的湖畔探索吧!public class ListActionWithoutPolymorphism { // 无多态的示例 void executeVectorActions(Vector<Object> vector) {/* 此处代码重复 */} void executeArrayListActions(ArrayList<Object> arrayList) {/* 此处代码重复 */} void executeLinkedListActions(LinkedList<Object> linkedList) {/* 此处代码重复 */} void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList) { /* 此处代码重复 */} } public class ListActionInvokerWithoutPolymorphism { listAction.executeVectorActions(new Vector<>()); listAction.executeArrayListActions(new ArrayList<>()); listAction.executeLinkedListActions(new LinkedList<>()); listAction.executeCopyOnWriteArrayListActions(new CopyOnWriteArrayList<>()); }

这段代码很糟糕,不是吗?想象维护它的难度!现在观察使用多态的相同示例:

public static void main(String … polymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(); } public class ListAction { void executeListActions(List<Object> list) { // 对不同列表执行操作 } } public class ListActionInvoker { public static void main(String... masterPolymorphism) { ListAction listAction = new ListAction(); listAction.executeListActions(new Vector<>()); listAction.executeListActions(new ArrayList<>()); listAction.executeListActions(new LinkedList<>()); listAction.executeListActions(new CopyOnWriteArrayList<>()); } }

多态的优势在于灵活性和扩展性。我们无需创建多个不同方法,只需声明一个接收通用List类型的方法。

多态方法调用与类型转换

可以在多态调用中调用特定方法,但会牺牲灵活性。示例如下:

欢迎大家来到IT世界,在知识的湖畔探索吧!public abstract class MetalGearCharacter { abstract void useWeapon(String weapon); } public class BigBoss extends MetalGearCharacter { @Override void useWeapon(String weapon) { System.out.println("Big Boss is using a " + weapon); } void giveOrderToTheArmy(String orderMessage) { System.out.println(orderMessage); } } public class SolidSnake extends MetalGearCharacter { void useWeapon(String weapon) { System.out.println("Solid Snake is using a " + weapon); } } public class UseSpecificMethod { public static void executeActionWith(MetalGearCharacter metalGearCharacter) { metalGearCharacter.useWeapon("SOCOM"); // 以下行无法工作 // metalGearCharacter.giveOrderToTheArmy("Attack!"); if (metalGearCharacter instanceof BigBoss) { ((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!"); } } public static void main(String... specificPolymorphismInvocation) { executeActionWith(new SolidSnake()); executeActionWith(new BigBoss()); } }

此处使用的技术是类型转换(casting),即在运行时显式改变对象类型。

注意:只有将通用类型强制转换为具体类型后,才能调用特定方法。这相当于明确告诉编译器:“我知道自己在做什么,因此要将对象转换为具体类型并使用特定方法。”

在上述示例中,编译器拒绝接受特定方法调用的原因很重要:传入的类可能是SolidSnake。在此情况下,编译器无法确保每个MetalGearCharacter的子类都声明了giveOrderToTheArmy方法。

保留关键字

注意保留字instanceof。在调用特定方法前,我们需检查MetalGearCharacter是否为BigBoss的实例。若BigBoss实例,将收到以下异常信息:

Exception in thread "main" java.lang.ClassCastException: com.javaworld.javachallengers.polymorphism.specificinvocation.SolidSnake cannot be cast to com.javaworld.javachallengers.polymorphism.specificinvocation.BigBoss

若需引用Java超类的属性或方法,可使用保留字super。例如:

欢迎大家来到IT世界,在知识的湖畔探索吧!public class JavaMascot { void executeAction() { System.out.println("The Java Mascot is about to execute an action!"); } } public class Duke extends JavaMascot { @Override void executeAction() { super.executeAction(); System.out.println("Duke is going to punch!"); } public static void main(String... superReservedWord) { new Duke().executeAction(); } }

在Duke的executeAction方法中使用super可调用超类方法,再执行Duke的特定动作。因此输出如下:

The Java Mascot is about to execute an action! Duke is going to punch!

多态的常见错误

  • 常见错误是认为无需类型默拒即可市貌和特定方法。
  • 另一个错误是在多态实例化类时不确认将调用哪个方法。需记住:被调用的方法是所创建实例的方法。
  • 还需注意方法重写不同于方法重载
  • 若参数不同,则无法重写方法。若返回类型是超类方法的子类,则可以修改重写方法的返回类型。

关于多态需要记住的要点

  • 所创建的实例将决定使用多态时调用哪个方法。
  • @Override注解强制程序员使用重写方法;否则将产生编译错误。
  • 多态可用于普通类、抽象类和接口。
  • 大多数设计模式依赖某种形式的多态。
  • 调用多态子类中特定方法的唯一方式是使用类型转换。
  • 可通过多态设计强大的代码结构。

接受Java多态挑战!

让我们测试你对多态和继承的理解。在此挑战中,你需要根据Matt Groening的辛普森一家代码推断每个类的输出。首先仔细分析以下代码:

欢迎大家来到IT世界,在知识的湖畔探索吧!public class PolymorphismChallenge { static abstract class Simpson { void talk() { System.out.println("Simpson!"); } protected void prank(String prank) { System.out.println(prank); } } static class Bart extends Simpson { String prank; Bart(String prank) { this.prank = prank; } protected void talk() { System.out.println("Eat my shorts!"); } protected void prank() { super.prank(prank); System.out.println("Knock Homer down"); } } static class Lisa extends Simpson { void talk(String toMe) { System.out.println("I love Sax!"); } } public static void main(String... doYourBest) { new Lisa().talk("Sax :)"); Simpson simpson = new Bart("D'oh"); simpson.talk(); Lisa lisa = new Lisa(); lisa.talk(); ((Bart) simpson).prank(); } }

你认为最终输出是什么?不要使用IDE!重点是提升代码分析能力,请自行推断结果。

选择你的答案后,你将在下方找到正确答案。

A)

 I love Sax! D'oh Simpson! D'oh

B)

欢迎大家来到IT世界,在知识的湖畔探索吧! Sax :) Eat my shorts! I love Sax! D'oh Knock Homer down

C)

 Sax :) D'oh Simpson! Knock Homer down

D)

欢迎大家来到IT世界,在知识的湖畔探索吧! I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down

解答挑战

对于以下方法调用:

new Lisa().talk("Sax :)");

输出为“I love Sax!”,因为我们向方法传递了字符串且Lisa类有此方法。

下一调用:

欢迎大家来到IT世界,在知识的湖畔探索吧!Simpson simpson = new Bart("D'oh"); simpson.talk();

输出为“Eat my shorts!”,因为我们用Bart实例化了Simpson类型。

以下调用较为复杂:

Lisa lisa = new Lisa(); lisa.talk();

此处通过继承使用了方法重载。由于未向talk方法传递参数,因此调用Simpson的talk方法,输出为:

欢迎大家来到IT世界,在知识的湖畔探索吧!"Simpson!"

最后一个调用:

((Bart) simpson).prank();

此例中,prank字符串在实例化Bart时通过new Bart(“D’oh”)传入。此时首先调用super.prank方法,再执行Bart的特定prank方法。输出为:

欢迎大家来到IT世界,在知识的湖畔探索吧!"D'oh""Knock Homer down"

因此正确答案是D。输出为:

I love Sax! Eat my shorts! Simpson! D'oh Knock Homer down

【注】本文译自:
https://www.infoworld.com/article/2255818/java-challengers-3-polymorphism-and-inheritance.html


免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/122145.html

(0)
上一篇 12小时前
下一篇 11小时前

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

联系我们YX

mu99908888

在线咨询: 微信交谈

邮件:itzsgw@126.com

工作时间:时刻准备着!

关注微信