java 中多态允许父类引用指向子类对象,因此当方法签名声明返回父类类型时,可以直接 return 一个子类实例。编译器会检查该子类是否确实是父类的子类型(继承或实现接口),如果是,则自动向上转型并返回。这样做的好处是调用者只能看到父类定义的公共行为,而实际执行的是子类重写的逻辑。

getShape() 返回 Shape 但实际是 Circle
在工厂模式或策略模式中,经常需要返回具体子类对象,但对外暴露统一接口。例如定义一个抽象类 Shape 和子类 Circle,方法返回 Shape 类型,内部创建 Circle 并返回。调用方接收到的变量类型为 Shape,但运行时对象是 Circle,可以调用 draw() 等被子类重写的方法。

abstract class Shape {
abstract void draw();
}
class Circle extends Shape {
@Override
void draw() {
System.out.println("Drawing Circle");
}
}
class Factory {
Shape getShape(String type) {
if ("circle".equals(type)) {
return new Circle(); // 返回子类对象,但类型是父类
}
return null;
}
}
public class Main {
public static void main(String[] args) {
Factory f = new Factory();
Shape s = f.getShape("circle");
s.draw(); // 输出: Drawing Circle
}
}
预期结果:
- s 的编译时类型为 Shape
- s 的运行时类型为 Circle
- s.draw() 调用的是 Circle 的实现
- 无异常抛出
Animal 引用返回 Dog 实例
另一个常见场景是集合操作或流处理中,过滤或转换后返回父类类型的列表。例如 List 中包含多个 Dog 对象,遍历每个元素时,虽然变量类型是 Animal,但实际对象是 Dog。这体现了多态的核心:同一接口,不同实现。

class Animal {
void speak() {
System.out.println("Animal sound");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("Woof");
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
List<animal> animals = Arrays.asList(new Dog(), new Dog());
for (Animal a : animals) {
a.speak(); // 每个都输出 Woof
}
}
}</animal>
预期结果:
- 列表类型为 List
- 每个元素实际为 Dog
- a.speak() 动态绑定到 Dog.speak()
- 输出两行 "Woof"
当子类未重写父类方法时会发生什么
如果子类没有重写父类中的某个方法,而通过父类引用调用该方法,则执行的是父类版本的代码。例如 Animal 有 move() 方法,Dog 未重写它,那么 Animal d = new Dog(); d.move(); 将调用 Animal.move()。这不影响返回值的多态性,只影响方法调用的行为。若需确保特定行为,应在子类中显式重写。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











