0

I am trying to figure out abstraction for a project on Sololearn and I reached module 5. I am asked to create two classes ( Square and Circle) both with contructors taking parameters and to set up an abstract method inherited from an abstract class in order to calculate the area. I feel really stupid as I can't figure out what am I doing wrong. Firstly, I tried writing @Override above the method as I thought it will work. Second, I tried changing the return type of the overridden abstract method to void from int/double but in my head it didn't make any sense as it should return a number, be that an int or a double. Anyway, here is the code, hopefully someone can shed a light on this dilemma:

import java.util.Scanner;

abstract class Shape {
    int width;
    abstract void area();
}
//your code goes here
class Square extends Shape{

    int area(int width){
        return width*width;
    }

    Square(int width){
        width = width;
    }
}
class Circle extends Shape{

    double PI = 3.14;
    double area(int width){
        return PI*width*width;
    }

    Circle(int width){
        width = width;
    }
}

public class Program {
    public static void main(String[ ] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        int y = sc.nextInt();
        
        Square a = new Square(x);
        Circle b = new Circle(y);
        a.area();
        b.area();
    }
} 
Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95

2 Answers2

1

You declared the abstract area method to accept no parameters and return void.

Then you declared a different area method in each class. They are different because they accept parameters and return different types than the declared abstract method.

To make this work you should have all the method return the same type and accept the same kind and number of parameters. I would suggest to declare the abstract method to accept no parameters and return a double, as in

abstract double area();

It should accept no parameters because what it needs depends on the child classes (in the case of a square, a side dimension would be sufficient, but not in the case of a rectangle, for example). So the method implementation in the subclasses should use the member variables of the child class itself to calculate an area.


As an aside, your constructors do nothing because you're assigning their parameter to themselves. You need a member variable in your class and you need to use this in the constructor to assign a value to them. So for example

class Square extends Shape {

    private int width;

    Square(int width) {
        this.width = width;
    }

    @Override
    double area() {
        return width * width;
    }
}

Finally, remember that what you called a width in your Circle class is not the circle's width, but its radius, so name that member variable accordingly.

EDIT About your comment on method parameters:

The point is, at the moment you have two subclasses that only need one parameter to calculate an area (width for a square, radius for a circle). But what if tomorrow you need a Rectangle class? To calculate its area you'll need two parameters, so you'll need to have two area methods. One that accepts one parameter, and another that accepts two. Leaving this specific example about shapes aside, that solution doesn't scale and will erase any advantage of using inheritance (or programming to an interface) in the first place, specifically polymorphism, i.e. the ability to do something like this:

Shape s = new Square(5);
System.out.println(s.area());

And then change only the declaration of what s is without changing the rest of the code

Shape s = new Rectangle(5, 3);
System.out.println(s.area());

This small example doesn't really make justice to how helpful this property is. For a more complete explanation take a look at What does it mean to "program to an interface".

All of this to say that it's helpful to use the internal state of the object to do computations that change between subclasses instead of passing a possibly variable number of parameters to their methods when what's being computed depends on a property of the object (i.e. its state) and not some external variable.

Federico klez Culloca
  • 26,308
  • 17
  • 56
  • 95
  • Hello, I have made some changes to the code according to your suggestions. I think the original problem didn't want me to change the definition of the abstract method. Anyway, I did change the constructor, the method body and defined a class variable but right now I get no output... – Mihailescu Fanush Jul 26 '21 at 09:59
  • Isn't the method supposed to get a parameter? – Mihailescu Fanush Jul 26 '21 at 10:00
  • @MihailescuFanush about the fact that it shows nothing, you need a `System.out.println` somewhere to print the result of calling a method. Simply calling a method does not print its result. – Federico klez Culloca Jul 26 '21 at 10:04
  • @MihailescuFanush about the parameters, I edited my answer so it's hopefully more clear. – Federico klez Culloca Jul 26 '21 at 10:13
0

Your super class method is void, but you implemented in child class method with return type, you need declare void method in child classes. You can use from @Override annotation, it's best practice for clean coding.

abstract class Shape {
    int width;
    abstract void area();
}

class Circle extends Shape{
    //Circle constructor
    public Circle(int width){
        this.width = width;
}
    @Override
    void area(){
        System.out.println(Math.PI * width * width);
 }
}

class Square extends Shape{
    //Square constructor
    public Square(int width){
        this.width = width;
}
    @Override
    void area(){
        System.out.println(width * width);
  }
}

public class Program {
    public static void main(String[ ] args) {
        Scanner sc = new Scanner(System.in);
        int x = sc.nextInt();
        int y = sc.nextInt();
    
        Square a = new Square(x);
        Circle b = new Circle(y);
        a.area();
        b.area();
 }
}