java中的封装继承多态
根据传说Venkat Subramaniam的说法,多态是面向对象编程中最重要的概念。 多态性(或对象根据其类型执行特殊操作的能力)使Java代码具有灵活性。 设计模式(例如Command , Observer , Decorator , Strategy以及由Gang of Four创建的许多其他模式)都使用某种形式的多态性。 精通此概念,可以极大地提高您思考编程挑战解决方案的能力。
获取代码
您可以获取此挑战的源代码并在此处运行自己的测试: https : //github.com/rafadelnero/javaworld-challengers
多态性的接口和继承
借助此Java Challenger,我们将专注于多态与继承之间的关系。 要记住的主要事情是多态性需要继承或接口实现 。 您可以在下面的示例中看到这一点,其中包括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();
}
}
此代码的输出将是:
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
类。 它不知道每个Sweet
的实现。 这种分离使我们可以灵活地更新和重用我们的类,并使代码易于维护。 设计代码时,请始终寻找使代码尽可能灵活和可维护的方法。 多态性是用于这些目的的非常强大的技术。
提示 : @Override
注释强制程序员使用必须重写的相同方法签名。 如果未重写该方法,则将出现编译错误。
方法覆盖中的协变返回类型
如果它是协变类型,则可以更改覆盖方法的返回类型。 协变类型基本上是返回类型的子类。 考虑一个例子:
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 Collections API的以下代码示例:
public class ListActionWithoutPolymorphism {
// Example without polymorphism
void executeVectorActions(Vector<Object> vector) {/* Code repetition here*/}
void executeArrayListActions(ArrayList<Object> arrayList) {/*Code repetition here*/}
void executeLinkedListActions(LinkedList<Object> linkedList) {/* Code repetition here*/}
void executeCopyOnWriteArrayListActions(CopyOnWriteArrayList<Object> copyOnWriteArrayList)
{ /* Code repetition here*/}
}
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) {
// Execute actions with different lists
}
}
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
类型的方法。
在多态方法调用中调用特定方法
可以在多态调用中调用特定的方法,但是这样做会牺牲灵活性。 这是一个例子:
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");
// The below line wouldn't work
// metalGearCharacter.giveOrderToTheArmy("Attack!");
if (metalGearCharacter instanceof BigBoss) {
((BigBoss) metalGearCharacter).giveOrderToTheArmy("Attack!");
}
}
public static void main(String... specificPolymorphismInvocation) {
executeActionWith(new SolidSnake());
executeActionWith(new BigBoss());
}
}
我们在这里使用的技术是强制转换 ,或在运行时故意更改对象类型。
请注意, 只有在将泛型类型转换为特定类型时,才可以调用特定方法。 一个很好的类比是明确地对编译器说的:“嘿,我知道我在这里做什么,所以我将对象转换为特定类型并使用特定方法。”
参考上面的示例,一个重要原因是编译器拒绝接受特定的方法调用:正在传递的类可能是SolidSnake
。 在这种情况下,编译器无法确保MetalGearCharacter
每个子类都MetalGearCharacter
了giveOrderToTheArmy
方法。
instanceof
保留关键字
注意保留字instanceof
。 调用特定的方法之前,我们已经问MetalGearCharacter
是“ instanceof
” 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
super
保留关键字
如果我们想引用Java超类的属性或方法怎么办? 在这种情况下,我们可以使用super
保留字。 例如:
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!
接受多态挑战!
让我们尝试一下您对多态性和继承的了解。 在这个挑战中,您将获得Matt Groening的《辛普森一家》中的几种方法,而您的挑战是推导出每个类的输出。 首先,请仔细分析以下代码:
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) Sax :)
Eat my shorts!
I love Sax!
D'oh
Knock Homer down
C) Sax :)
D'oh
Simpson!
Knock Homer down
D) I love Sax!
Eat my shorts!
Simpson!
D'oh
Knock Homer down
刚才发生了什么? 了解多态
对于以下方法调用:
new Lisa().talk("Sax :)");
输出将是“ I love Sax!
”这是因为我们正在将String
传递给方法,而Lisa
拥有了该方法。
对于下一个调用:
Simpson simpson = new Bart("D'oh");
simpson.talk();
输出将是“ Eat my shorts!
”,这是因为我们正在用Bart
实例化Simpson
类型。
现在检查一下,这有点棘手:
Lisa lisa = new Lisa();
lisa.talk();
在这里,我们使用带有继承的方法重载。 我们没有将任何内容传递给talk方法,这就是调用Simpson
talk
方法的原因。 在这种情况下,输出将是:
"Simpson!"
还有一个:
((Bart) simpson).prank();
在这种情况下,当我们用new Bart("D'oh");
实例化Bart
类时,会传递prank String
new Bart("D'oh");
。 在这种情况下,首先将super.prank
方法将被调用,接着特定prank
从方法Bart
。 输出将是:
"D'oh"
"Knock Homer down"
视频挑战! 调试Java多态性和继承
调试是完全吸收编程概念并改善代码的最简单方法之一。 在此视频中,您可以在调试和解释Java多态性挑战时继续进行:
多态性的常见错误
认为可以在不使用强制转换的情况下调用特定方法是一个常见的错误。
另一个错误是不确定多态实例化类时将调用哪种方法。 请记住,要调用的方法是创建的实例的方法。
还要记住,方法重载不是方法重载。
如果参数不同,则无法覆盖方法。 如果返回类型是超类方法的子类, 则可以更改重写方法的返回类型。
关于多态性要记住什么
- 创建的实例将确定使用多态时将调用哪种方法。
-
@Override
注释强制程序员使用覆盖的方法; 否则,将出现编译器错误。 - 多态可以与普通类,抽象类和接口一起使用。
- 大多数设计模式取决于某种形式的多态性。
- 在多态子类中使用特定方法的唯一方法是使用强制转换。
- 使用多态性可以在代码中设计功能强大的结构。
- 运行测试。 这样做,您将能够掌握这个强大的概念!
答案键
这个Java挑战者的答案是D。 输出为:
I love Sax!
Eat my shorts!
Simpson!
D'oh
Knock Homer down
这个故事“ Java中的多态与继承”最初是由JavaWorld发布的 。
翻译自: https://www.infoworld.com/article/3290403/java-challengers-3-polymorphism-and-inheritance.html
java中的封装继承多态