Warm tip: This article is reproduced from serverfault.com, please click

java-如何添加方法并拦截来自其他类的私有字段

(java - How to add method and intercept private fields from a different class)

发布于 2020-12-07 04:02:52

我想介绍一个Shape类作为类父接口CircleRectangle我必须实现getName()Circle为该Circle对象和RectangleRectangle对象返回的方法另外,我必须toString()CircleRectangle中都覆盖该方法这些toString方法将调用该getName()方法,并将生成一个表示对象的字符串,如下所示:

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

此外,我不能修改的类ShapeRectangleCircle或者Main,为此你会发现下面的代码。我不确定如何执行此操作。有人可以帮帮我吗?

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

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

问题

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());
        }
    }
}
Questioner
Jennifer
Viewed
11
dreamcrash 2020-12-29 04:24:35

我想将Shape类作为Circle和Rectangle类的父接口引入

为此,你需要使用类型间声明,这是AspectJ静态横切功能,该功能允许更改类的结构,即添加新方法,使类实现接口等。你已正确完成以下操作:

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

我必须实现getName()方法,该方法将为Circle对象返回“ Circle”,为Rectangle对象返回“ Rectangle”。

你还正确地做到了:

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 + ")";
}

你有两个问题

  1. 该方法getName()不是静态的,因此将Rectangle.getName()都更改 Circle.getName()this.getName();

  2. 田野widthheightradius私有因此,你不能简单地从这样的方面访问它们。来自来源

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

尽管这些限制适用于许多方面,但在某些方面,建议或类型间成员可能需要访问其他类型的私有或受保护资源。为此,可以将方面声明为特权特权方面的代码可以访问所有成员,甚至是私有成员

解决此问题,你(至少)有3个选择:

  1. 公开这些领域;
  2. 使AspectQuestion 享有特权;
  3. 吸引那些私有领域的人。

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

在类中Rectangle,为widthheight字段添加吸气剂

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类,你将在其中找到以下代码。

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

因此,你将使该方面具有Question 特权

 privileged public aspect Question

我与一些作者有相同的看法:

  • JD Gradecki和N.Lesiecki。精通AspectJ:Java的面向方面的编程》一书中的内容
  • 拉达德(R. Laddad)。在《 AspectJ实际应用:带有Spring应用程序的企业AOP》一书中。

特权方面应避免尽可能多的,因为它们增加一个
方面和类之间的内在相关性,他们绕过能见度规则,应用在目标类。