I'm working on a tiny exercise java program that calculates circle and square (classes) area, that implements surface (interface) which has a method called area(). A requirement is that I have to implement a class called SumArea that has a generic method called calcArea() that receives Circle circ[] and Square square[] arrays and executes area calculation.
Program structure:
-> UseSumArea.java (main method)
-> Surface.java (interface)
-> Square.java (class that implements Surface.java)
-> Circle.java (class that implements Surface.java)
-> SumArea.java (class that executes calcArea() method)
UseSumArea.java
public class UseSumArea {
public static void main(String[] args) {
Square square[] = { new Square(2.0), new Square(5.0) };
Circle circ[] = { new Circle(3.0), new Circle(2.0) };
Surface surf[] = new Surface[square.length circ.length];
surf[0] = square[0];
surf[1] = square[1];
surf[2] = circ[0];
surf[3] = circ[1];
SumArea sum = new SumArea();
System.out.println("Square's sum area = " sum.calcArea(square));
System.out.println("Circle's sum area = " sum.calcArea(circ));
System.out.println("Surface's sum area = " sum.calcArea(surf));
}
}
Surface.java
public interface Surface {
public double area();
}
Square.java
public class Square implements Surface {
private double area;
private double side;
public Square(double l) {
this.side = l;
area();
}
@Override
public double area() {
return this.area = (this.side)*(this.side);
}
public double getArea() {
return area;
}
public void setArea(double area) {
this.area = area;
}
public double getSide() {
return side;
}
public void setSide(double side) {
this.side = side;
}
}
Circle.java
public class Circle implements Surface {
private double area;
private double radius;
public Circle (double r) {
this.radius = r;
area();
}
@Override
public double area() {
return area = (((this.radius)*(this.radius))*(Math.PI));
}
public double getRadius() {
return radius;
}
public void setRadius(double raio) {
this.raio = raio;
}
public double getArea() {
return area;
}
public void setArea(double area) {
this.area = area;
}
}
SumArea.java
public class SumArea {
private double area;
public <T> double calcArea(T[] t) { //generic method that receives Square and Circle arrays
double arrayArea = 0;
for (T a : t) {
arrayArea = arrayArea (a.area());
}
return this.area = arrayArea;
}
}
My doubt is over this SumArea's code snippet:
arrayArea= arrayArea (a.area());
How can I access the area() method of each Circle and Square objects inside this generic method?
CodePudding user response:
You need to bound the type variable:
public <T extends Surface> double calcArea(T[] t) {
or just declare the parameter as an array of Surface
s:
public double calcArea(Surface[] t) {
Note that the latter is preferable because generics and arrays don't play very nicely together. If you were to need to have a type variable for other reasons, it would be advisable to change to a Collection
, or similar:
public <T extends Surface> double calcArea(Collection<T> t) {
(And, as a minor matter of preference, I would use S
rather than T
to name a type variable which extends Surface
)
CodePudding user response:
Since the problem in regard to generic types is already addressed by Andy Turner, I just want to add a suggestion related to the class design.
I think there is a bit of redundancy in how these classes were designed. You need to create an instance of SumArea
in order to do the calculation. And the result of the last of the last calcArea()
method call will be stored in this object (let's assume that this calculation is far more complex and CPU-consuming).
But do we really need to store somewhere else the value is already returned by the method? In this case, the idea to cash the history of calculations (as a single variable or as a collection of values) doesn't seem to be useful because it can't be reused without knowing which objects were involved in the calculation.
And without storing the result this method will not be bound to a state, i.e. it has to be static. And since interfaces can have static methods, instead of creating a utility class for that purpose it could be placed in the Surface
interface. Like that.
public interface Surface {
public double area();
public static <T extends Surface> double calcArea(T[] t) { // generic method that receives Square and Circle arrays
double arrayArea = 0;
for (T a : t) {
arrayArea = a.area();
}
return arrayArea;
}
}
Note that static behavior declared in interfaces in contrast to classes could be invoked only by using the name of an interface:
System.out.println("Circle's sum area = " Surface.calcArea(circ));
Also note that it makes sense for both classes to have a field area
inside the classes Circle
and Square
only if other fields will be declared as final, i.e. they must be initialed only one during the object construction and setters become unnecessary.
In this case (assuming that radius
has been declared as final and is being validated when assigned so that reduce > 0
) method area()
will look like this:
@Override
public double area() {
if (area > 0) { // `0` is a default value for instance variables
return area; // reusing already calculated value
}
return area = radius * radius * Math.PI;
}
And there mustn't be two methods area()
and getArea()
leave either one or another.