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

How to add method and intercept private fields from a different class

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

此外,我无法修改 classes ShapeRectangleCircleMain,您可以在下面找到这些代码。我不确定该怎么做。有人可以帮我吗?

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

Shape.java

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

Rectangle.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 :(");
    }
}

Question.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());
        }
    }
}

I am trying to introduce a Shape class as a parent interface for the classes Circle and Rectangle

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

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

I have to implement getName() method that will return "Circle" for the Circle object and "Rectangle" for the Rectangle object.

你也做对了:

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

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

Also, I have to override the toString() method in both the Circle and Rectangle > classes.

你也做对了:

public String Rectangle.toString(){ (...)}

public String Circle.toString(){ (...)}

The toString methods will call the getName() method and will generate a string representing the object as follows: Circle with radius of 2 is represented as "Circle(2)" Rectangle with width of 2 & height of 10 is represented as "Rectangle(2, 10)".

这一步你做错了:

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

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

你有两个问题,

  1. 方法 getName() 不是 static,因此两者都要更改 Rectangle.getName()Circle.getName()this.getName();

  2. 字段 widthheightradiusprivate。因此,您不能简单地从那样的方面访问它们。来自 source:

Code written in aspects is subject to the same access control rules as Java code when referring to members of classes or aspects. So, for example, code written in an aspect may not refer to members with default (package-protected) visibility unless the aspect is defined in the same package.

While these restrictions are suitable for many aspects, there may be some aspects in which advice or inter-type members needs to access private or protected resources of other types. To allow this, aspects may be declared privileged. Code in priviliged aspects has access to all members, even private ones.

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

  1. 将这些字段设置为 public;
  2. 使方面 Question 特权;
  3. 为这些私有字段创建 getters。

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

向 class Rectangle 添加 widthheight 字段的 getter:

public double getWidth() {return this.width;}

public double getHeight() {return this.height;}

并在 class 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()+ ")";
}

Also, I cannot modify the classes Shape, Rectangle, Circle or Main, for which you will find the codes bellow.

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

因此,您只剩下 Question 特权:

 privileged public aspect Question

我同意一些作者的观点:

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

应尽可能避免 特权 方面,因为它们添加了
方面和 classes 之间的内在依赖性,它们 规避 应用于该目标的可见性规则 class.