面试题答案
一键面试泛型类型体系设计
- 定义基础图形类和绘制策略接口
- 定义抽象的
Shape
类作为所有图形类的基类:
public abstract class Shape { // 图形的通用属性和方法可以在此定义 }
- 定义
DrawingStrategy
接口,它是一个泛型接口,接受一个Shape
的子类作为类型参数:
public interface DrawingStrategy<T extends Shape> { void draw(T shape); }
- 定义抽象的
- 定义具体图形类和对应的绘制策略类
- 定义
Circle
类继承自Shape
:
public class Circle extends Shape { private double radius; public Circle(double radius) { this.radius = radius; } public double getRadius() { return radius; } }
- 定义
CircleDrawingStrategy
类实现DrawingStrategy<Circle>
:
public class CircleDrawingStrategy implements DrawingStrategy<Circle> { @Override public void draw(Circle circle) { System.out.println("Drawing a circle with radius " + circle.getRadius()); } }
- 同样地,定义
Rectangle
类和RectangleDrawingStrategy
类:
public class Rectangle extends Shape { private double width; private double height; public Rectangle(double width, double height) { this.width = width; this.height = height; } public double getWidth() { return width; } public double getHeight() { return height; } }
public class RectangleDrawingStrategy implements DrawingStrategy<Rectangle> { @Override public void draw(Rectangle rectangle) { System.out.println("Drawing a rectangle with width " + rectangle.getWidth() + " and height " + rectangle.getHeight()); } }
- 定义
继承关系设计
- 图形类继承关系:所有具体图形类(如
Circle
、Rectangle
)继承自抽象类Shape
。这种继承关系使得我们可以在框架中以统一的方式处理不同类型的图形,例如在需要处理多种图形的集合或方法参数中,可以使用Shape
类型来接受所有具体图形。 - 绘制策略类继承关系:每个具体图形的绘制策略类(如
CircleDrawingStrategy
、RectangleDrawingStrategy
)实现DrawingStrategy
接口,且类型参数为对应的具体图形类。这种设计保证了类型安全,因为每个绘制策略只处理特定类型的图形。
调用逻辑设计
- 创建图形和对应的绘制策略
Circle circle = new Circle(5.0); DrawingStrategy<Circle> circleStrategy = new CircleDrawingStrategy(); Rectangle rectangle = new Rectangle(4.0, 6.0); DrawingStrategy<Rectangle> rectangleStrategy = new RectangleDrawingStrategy();
- 绘制图形
circleStrategy.draw(circle); rectangleStrategy.draw(rectangle);
- 更通用的处理方式:可以使用一个
Map
来存储图形类型和对应的绘制策略,以实现动态选择绘制策略。
然后可以这样使用:import java.util.HashMap; import java.util.Map; public class GraphicsFramework { private Map<Class<? extends Shape>, DrawingStrategy<? extends Shape>> strategyMap = new HashMap<>(); public GraphicsFramework() { strategyMap.put(Circle.class, new CircleDrawingStrategy()); strategyMap.put(Rectangle.class, new RectangleDrawingStrategy()); } public void drawShape(Shape shape) { DrawingStrategy<? extends Shape> strategy = strategyMap.get(shape.getClass()); if (strategy != null) { strategy.draw(shape); } else { System.out.println("No drawing strategy found for this shape."); } } }
GraphicsFramework framework = new GraphicsFramework(); framework.drawShape(circle); framework.drawShape(rectangle);
避免泛型相关陷阱
- 类型擦除问题:Java的泛型是通过类型擦除实现的,即在运行时,泛型类型信息会被擦除。为了避免由此导致的问题:
- 在泛型类或接口中避免依赖运行时的泛型类型信息:例如,不要在泛型类中使用
instanceof
操作符判断泛型类型,因为在运行时泛型类型信息已被擦除。如果需要判断类型,可以通过传递Class
对象来实现。比如在drawShape
方法中,可以将Class
对象作为参数传递给绘制策略的获取逻辑。 - 使用通配符和受限类型参数:在定义方法参数或返回值时,合理使用通配符(如
?
)和受限类型参数(如<T extends Shape>
)来确保类型安全。例如,在GraphicsFramework
的drawShape
方法中,使用DrawingStrategy<? extends Shape>
作为类型,这样可以接受任何具体图形的绘制策略,同时保证类型安全。 - 利用反射获取类型信息:如果确实需要在运行时获取泛型类型信息,可以结合反射机制。例如,通过
ParameterizedType
接口获取泛型类型参数的实际类型,但这种方法应谨慎使用,因为反射操作通常会带来性能开销。
- 在泛型类或接口中避免依赖运行时的泛型类型信息:例如,不要在泛型类中使用