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:
Circle
with radius of 2 is represented as"Circle(2)"
Rectangle
with 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
, andradius
are private. And therefore you cannot simply access them from the aspect like that. From source:To solve this, you have (at least) 3 options:
Question
privileged;From an
OOP
encapsulation point of view, the third option is the best. Which would look like this:To the class
Rectangle
add the getters for thewidth
andheight
fields:and to the class
Circle
add the getter for theradius
field:Finally, adapt the aspect
Question
accordingly: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
Question
privileged: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.