029.Polymorphism 多态

本文通过具体的Objective-C代码示例介绍了多态的概念及其工作原理。重点解释了编译时类型与运行时类型的区别,并展示了如何利用多态特性实现方法的重写与调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

---------------  FKBase.h  ---------------
#import <Foundation/Foundation.h>
@interface FKBase : NSObject
- (
void) base;
- (
void) test;
@end
---------------  FKBase.m  ---------------
#import "FKBase.h"
@implementation FKBase
- (
void) base
{
     NSLog(@"父类的普通base方法");
}
- (
void) test
{
     NSLog(@"父类的将被覆盖的test方法");
}
@end
---------------  FKSubclass.h  ---------------
#import <Foundation/Foundation.h>
#import "FKBase.h"
@interface FKSubclass : FKBase
- (
void) sub;
@end
---------------  FKSubclass.m  ---------------
#import "FKSubclass.h"
@implementation FKSubclass
- (
void) test
{
     NSLog(@"子类的覆盖父类的test方法");
}
- (
void) sub
{
     NSLog(@"子类的sub方法");
}
@end
---------------  main.m  ---------------
#import <Foundation/Foundation.h>
#import
"FKSubclass.h"
int main()
{
   
// 下面编译时类型和运行时类型完全一样,因此不存在多态
    FKBase* bc = [[FKBase alloc] init];
   
// 下面两次调用将执行FKBase的方法
    [bc base];
    [bc test];
   
// 下面编译时类型和运行时类型完全一样,因此不存在多态
    FKSubclass* sc = [[FKSubclass alloc] init];
   
// 下面调用将执行从父类继承到的base方法
    [sc base];
   
// 下面调用将执行子类重写的test方法
    [sc test];
   
// 下面调用将执行子类定义的sub方法
    [sc sub];
   
// 下面编译时类型和运行时类型不一样,多态发生
    FKBase* ploymophicBc = [[FKSubclass alloc] init];
   
// 下面调用将执行从父类继承到的base方法
    [ploymophicBc base];
   
// 下面调用将执行子类重写的test方法
    [ploymophicBc test];
   
// 因为ploymophicBc的编译类型是FKBase
   
// FKBase类没有提供sub方法,所以下面代码编译时会出现错误。
    // [ploymophicBc sub];
    // 可将任何类型的指针变量赋值给id类型的变量
   
id dyna = ploymophicBc;
    [dyna sub];
}

一、编写本节代码的具体步骤:
1.可仿照第二章001节的代码编写步骤,不同的是,本节代码要创建两个类。

二、本节代码涉及到的知识点:
1.OC指针类型的变量有两个,一个是编译时的类型,一个是运行时的类型。
2.编译时的类型,由声明该变量时使用的类型决定。
3.运行时的类型,由实际赋给该变量的对象决定。
4.如果编译时类型和运行时类型不一致,就可能出现多态。
5.上面的代码中,ploymophicBc是一个指针变量,它在编译时的类型是FKBase,
  而运行时的类型是FKSubclass。当调用该指针的test方法时,实际执行的将是子类中重写的方法。
6.id类型的变量可以调用该变量实际所指对象的方法。所以上面代码中的最后一句可以执行。
<think>我们正在处理一个Java类型转换异常:`Dly.polymorphism.exe1.GeometricObject` 无法转换为 `Dly.polymorphism.exe1.MyRectangle`。 这种错误通常发生在试图将一个父类对象强制转换为子类对象,而该父类对象实际上并不是该子类的实例时。 步骤分析: 1. 理解继承关系:`MyRectangle` 应该是 `GeometricObject` 的子类(或间接子类)。因此,一个`MyRectangle`对象同时也是一个`GeometricObject`对象,但反过来则不一定成立。 2. 错误场景:我们尝试将一个实际类型为`GeometricObject`(或其它非`MyRectangle`的子类)的对象强制转换为`MyRectangle`类型。 3. 解决方案: a. 确保在转换前,该对象确实是`MyRectangle`类型(或它的子类)的实例。这可以通过使用`instanceof`操作符来检查。 b. 如果对象不是目标类型,则不能进行强制转换,需要采取其他措施(如重新设计代码以避免转换,或者处理可能的错误情况)。 示例代码: 假设我们有以下类结构: ```java class GeometricObject { ... } class MyRectangle extends GeometricObject { ... } ``` 错误代码可能是这样的: ```java GeometricObject obj = new GeometricObject(); // 或者可能是其他子类对象,但不是MyRectangle MyRectangle rect = (MyRectangle) obj; // 这里会抛出ClassCastException ``` 修正方法: ```java if (obj instanceof MyRectangle) { MyRectangle rect = (MyRectangle) obj; // 安全地使用rect } else { // 处理不是MyRectangle的情况 } ``` 4. 另一种情况:可能你期望某个方法返回的是`MyRectangle`,但实际上返回的是`GeometricObject`,并且该对象不是`MyRectangle`的实例。这时需要检查返回该对象的地方,确保返回的是正确的类型。 5. 设计建议:如果经常需要将父类转换为特定子类,可能意味着设计上有问题。考虑使用多态(在父类中定义抽象方法或虚方法,在子类中重写)来避免类型转换。 根据引用[3]中的数据范围提示(虽然看起来是无关的),我们应确保代码的健壮性,特别是在处理类型转换时。 因此,具体解决步骤: 1. 找到抛出异常的代码行。 2. 检查该行中试图转换的对象的实际类型(可以在转换前打印它的类名:`obj.getClass().getName()`)。 3. 根据实际类型,决定是添加类型检查(`instanceof`)还是修改代码逻辑以避免转换。 如果问题仍然存在,请提供相关代码片段,以便进一步分析。 相关问题: 1. Java中如何安全地进行类型转换? 2. 如何避免在Java中使用类型转换? 3. Java中`instanceof`操作符的使用场景是什么? 4. 什么是Java中的多态?如何利用多态避免类型转换?</think>### Java 类型转换异常解决方案 在 Java 中遇到 `GeometricObject 无法转换为 MyRectangle` 的错误,通常发生在尝试将父类对象强制转换为子类对象时,但该对象实际上不是子类的实例。以下是详细分析和解决方案: #### 错误原因分析 1. **继承关系问题** - 假设 `MyRectangle extends GeometricObject`(子类继承父类) - 当尝试将 `GeometricObject` 实例强制转换为 `MyRectangle` 时,如果该实例不是 `MyRectangle` 对象,就会抛出 `ClassCastException` 2. **常见错误场景**: ```java GeometricObject obj = new GeometricObject(); // 父类实例 MyRectangle rect = (MyRectangle) obj; // 抛出异常 ``` 3. **根本原因**: Java 允许向上转型(子类转父类),但向下转型(父类转子类)需要对象**原本就是目标类型**的实例。 --- #### 解决方案 ##### 方法 1:使用 `instanceof` 检查类型 ```java if (obj instanceof MyRectangle) { MyRectangle rect = (MyRectangle) obj; // 安全转换 rect.draw(); // 调用子类方法 } else { System.out.println("对象不是MyRectangle类型"); } ``` ##### 方法 2:重新设计类结构(推荐) 1. **使用多态避免强制转换** 在父类中声明通用方法,子类实现具体行为: ```java class GeometricObject { public void draw() { /* 默认实现或抽象方法 */ } } class MyRectangle extends GeometricObject { @Override public void draw() { /* 矩形绘制逻辑 */ } } // 使用时直接调用,无需转换 GeometricObject obj = new MyRectangle(); obj.draw(); // 自动调用MyRectangle的draw() ``` 2. **工厂模式创建对象** 确保对象创建时就是目标类型: ```java class ShapeFactory { public GeometricObject createShape(String type) { if ("rectangle".equals(type)) { return new MyRectangle(); // 直接返回子类实例 } return new GeometricObject(); } } ``` ##### 方法 3:检查对象来源 - 如果对象来自集合(如 `List<GeometricObject>`),确保存入时已是子类实例: ```java List<GeometricObject> shapes = new ArrayList<>(); shapes.add(new MyRectangle()); // ✅ 正确:存入子类对象 // 取出时检查类型 GeometricObject obj = shapes.get(0); if (obj instanceof MyRectangle) { ((MyRectangle) obj).calculateArea(); // 安全调用 } ``` --- #### 预防措施 1. **避免不必要的转型**:优先使用多态而非强制转换 2. **使用泛型约束类型**: ```java class Container<T extends GeometricObject> { private T shape; public T getShape() { return shape; } // 直接返回正确类型 } Container<MyRectangle> rectContainer = new Container<>(); MyRectangle rect = rectContainer.getShape(); // 无需转换 ``` 3. **代码审查重点**: 检查所有 `(MyRectangle)` 强制转换的位置,添加 `instanceof` 验证 > **关键原则**:向下转型(父类转子类)是高风险操作,应通过设计规避。优先通过多态、泛型等机制保证类型安全[^1]。 --- ### 相关问题 1. Java 中 `instanceof` 和 `isInstance()` 有何区别? 2. 如何通过泛型避免 Java 类型转换异常? 3. 多态在面向对象设计中有哪些应用场景? 4. 为什么 `List<String>` 不能强制转换为 `List<Object>`? [^1]: 引用自面向对象编程设计原则 [^2]: Java 类型系统规范 [^3]: Java 泛型类型擦除机制
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值