如何添加方法并拦截来自不同类的私有字段

Posted

技术标签:

【中文标题】如何添加方法并拦截来自不同类的私有字段【英文标题】:How to add method and intercept private fields from a different class 【发布时间】:2020-12-07 04:02:52 【问题描述】:

我正在尝试引入Shape 类作为CircleRectangle 类的父接口。我必须实现getName() 方法,该方法将为Circle 对象返回Circle,为Rectangle 对象返回Rectangle。此外,我必须在 CircleRectangle 类中覆盖 toString() 方法。 toString 方法将调用getName() 方法并生成一个表示对象的字符串,如下所示:

半径为 2 的 Circle 表示为 "Circle(2)" Rectangle 宽度为 2,高度为 10 表示为 "Rectangle(2, 10)"

另外,我无法修改 ShapeRectangleCircleMain 类,您将在下面找到代码。我不确定如何做到这一点。有人可以帮帮我吗?

这是我到目前为止所做的:

Shape.java

public interface Shape 
    String getName();
    double getPerimeter();
    double getArea();

矩形.java

public class Rectangle 
    private double width, height;
    public Rectangle(double width, double height) 
        this.width = width;
        this.height = height;
    
    public double getPerimeter() 
        return 2 * (this.width + this.height);
    
    public double getArea() 
        return this.width * this.height;
    

Circle.java

public class Circle
    private double radius;
    public Circle(double radius) 
        this.radius = radius;
    
    public double getPerimeter() 
        return 2 * Math.PI * this.radius;
    
    public double getArea() 
        throw new RuntimeException("Oops, I don't know how to calculate this :(");
    

问题.aj

public aspect Question 
    declare parents: Rectangle implements Shape;
    declare parents: Circle implements Shape;
    
    public String Rectangle.getName()
        return "Rectangle";
    
    
    public String Circle.getName()
        return "Circle";
    
    
    public String Rectangle.toString()
        return Rectangle.getName()+"(" + this.width +", " + this.height +")";
    
    
    public String Circle.toString()
        return Circle.getName() + "(" + this.radius + ")";
    

Main.java

public class Main 
    public static void main(String[] args) 
        try 
            Shape s;
            s = (Shape) new Rectangle(2, 10);
            System.out.println("The area of " + s + " is " + s.getArea());
            s = (Shape) new Rectangle(-2, 10);
            System.out.println("The perimeter of " + s + " is " + s.getPerimeter());
            s = (Shape) new Circle(-2);
            System.out.println("The perimeter of " + s + " is " + s.getPerimeter());
            s = (Shape) new Circle(2);
            System.out.println("The area of " + s + " is " + s.getArea());
        
        catch(Exception e) 
            System.out.println("Error: " + e.getMessage());
        
    

【问题讨论】:

【参考方案1】:

我正在尝试引入一个 Shape 类作为父接口 类圆形和矩形

为此,您需要使用 inter-type declarations 一个 AspectJ 静态横切 功能,该功能允许更改类的结构,即添加新方法,使类实现接口等等。你做对了:

declare parents: Rectangle implements Shape;
declare parents: Circle implements Shape;

我必须实现 getName() 方法,该方法将为 圆形对象和矩形对象的“矩形”。

你也做对了:

public String Rectangle.getName()
    return "Rectangle";


public String Circle.getName()
    return "Circle";

另外,我必须重写 Circle 和 Rectangle > 类中的 toString() 方法。

你也做对了:

public String Rectangle.toString() (...)

public String Circle.toString() (...)

toString 方法将调用 getName() 方法并生成 表示对象的字符串,如下所示:半径为 2 的圆 表示为“Circle(2)”矩形,宽度为 2,高度为 10 表示为“Rectangle(2, 10)”。

这一步你做错了:

public String Rectangle.toString()
    return Rectangle.getName()+"(" + this.width +", " + this.height +")";


public String Circle.toString()
    return Circle.getName() + "(" + this.radius + ")";

你有两个问题,

    方法getName() 不是静态,因此同时更改 Rectangle.getName()Circle.getName()this.getName();

    widthheightradius 字段是私有。因此,您不能简单地从这样的方面访问它们。来自source:

方面编写的代码受制于相同的访问控制规则 引用类或方面成员时的 Java 代码。 因此,对于 例如,在一个方面编写的代码可能不会引用具有 默认(包保护)可见性,除非方面在 同一个包。

虽然这些限制适用于许多方面,可能有 建议或跨类型成员需要访问的某些方面 其他类型的私有或受保护资源。为了允许这一点,方面 可以声明特权。特权方面的代码可以访问 所有成员,甚至私人成员

解决这个问题,您有(至少)3 个选项:

    公开这些字段; 使方面Question 特权; 为这些私有字段创建 getter。

OOP 封装的角度来看,第三个选项是最好的。看起来像这样:

Rectangle 类添加widthheight 字段的getter:

public double getWidth() return this.width;

public double getHeight() return this.height;

Circle 类中添加radius 字段的getter:

public double getRadius() return this.radius;

最后,相应地调整切面Question

public String Rectangle.toString()
    return this.getName()+"(" + this.getWidth() +", " + this.getHeight() +")";


public String Circle.toString()
    return this.getName() + "(" + this.getRadius()+ ")";

另外,我无法修改 Shape、Rectangle、Circle 或 Main 类, 您将在下面找到代码。

好吧,这不包括方法 1)(无论如何都很糟糕)和 3)(这是最好的 IMO)。

因此,您只能将方面设置为Question 特权

 privileged public aspect Question

我同意一些作者的相同观点:

J. D. Gradecki 和 N. Lesiecki。 Mastering AspectJ: Aspect-Oriented Programming in Java 一书中。 R.拉达德。在 AspectJ in Action: Enterprise AOP with Spring Applications 一书中。

应尽可能避免特权方面,因为它们增加了 方面和类之间的内在依赖关系,并且它们规避应用于该目标类的可见性规则。

【讨论】:

以上是关于如何添加方法并拦截来自不同类的私有字段的主要内容,如果未能解决你的问题,请参考以下文章

如何使来自不同类的文本字段等于相同的文本?

Cocoa:将子视图添加到来自不同类和 nib 的视图

在Python中触发来自不同类的Tkinter事件

如何在 Java 中使用来自不同类的方法?

C++ 重载 == 比较来自不同类的对象

Firebase Auth 使用来自不同类的文本输入创建新用户