I am trying to introduce a Shape class as a parent interface for the classes Circle and Rectangle. I have to implement getName() method that will return Circle for the Circle object and Rectangle for the Rectangle object. Also, I have to override the toString() method in both the Circle and Rectangle classes. The toString methods will call the getName() method and will generate a string representing the object as follows:
Circlewith radius of 2 is represented as"Circle(2)"Rectanglewith width of 2 & height of 10 is represented as"Rectangle(2, 10)".
Also, I cannot modify the classes Shape, Rectangle, Circle or Main, for which you will find the codes below. I'm not sure about how to do this. Can someone please help me?
Here is what I have done so far:
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());
}
}
}
For that you need to use inter-type declarations an AspectJ static crosscutting feature that allows to change the structure of a class, namely adding new methods, make the class implement interfaces and so. Which you have done correctly:
You also did that correctly:
Which you also did it correctly:
This step you did it wrongly:
You have two problems,
the method
getName()is not static, therefore change bothRectangle.getName()andCircle.getName()tothis.getName();the fields
width,height, andradiusare private. And therefore you cannot simply access them from the aspect like that. From source:To solve this, you have (at least) 3 options:
Questionprivileged;From an
OOPencapsulation point of view, the third option is the best. Which would look like this:To the class
Rectangleadd the getters for thewidthandheightfields:and to the class
Circleadd the getter for theradiusfield:Finally, adapt the aspect
Questionaccordingly:Okey, this excludes the approaches 1) (which was bad anyway) and 3) (which as the best IMO).
Consequently, you are left with making the aspect
Questionprivileged:I share the same opinion of some authors:
that privileged aspects should be avoided as much as possible since they add an
intrinsic dependency between aspects and classes, and they circumvent the visibility rules that where applied to that target class.