user_01 user_01 - 15 days ago 10
Java Question

How to implement one generic method for two classes in java

I have an interface that has one ordinary method and one generic method. I have implemented ordinary method for two different classes, but do not now how to do that with generic method. Here is my code:

Sphere.java:

public class Sphere implements GeometricShape<Sphere> {

private double radius;

public Sphere (double radius) {
this.radius = radius;
}

public double volume() {
return (4.0 / 3.0) * Math.PI * radius * radius * radius;
}

public void describe() {
System.out.println("Sphere[radius=" + radius + "]");
}

@Override
public Sphere supersize()
{
this.radius*=2;
return new Sphere(radius);
}

}


Rectangle.java

public class Rectangle implements TwoDShape {

private double width, height;

public Rectangle (double width, double height) {
this.width = width;
this.height = height;
}

public double area()
{
return width * height;
}

public double perimeter()
{
return 2.0 * (width + height);
}

public void describe()
{
System.out.println("Rectangle[width=" + width + ", height=" + height + "]");
}

@Override
public Rectangle supersize()
{

this.width*=2;
this.height*=2;
return new Rectangle(width, height);
}


}


TwoDShape.java:

public interface TwoDShape extends GeometricShape
{
public double area();

}


ThreeDShape.java:

public interface ThreeDShape extends GeometricShape<ThreeDShape>
{
public double volume();
}


GeometricShape.java:

public interface GeometricShape<T extends GeometricShape<T>>
{
public void describe();
public T supersize();

}


and finally main class ArrayListExample.java:

import java.util.ArrayList;


public class ArrayListExample {



public static void describe_all( ArrayList<? extends GeometricShape> shapes )

{
for(int i=0;i<shapes.size();i++)
{
shapes.get(i).describe();

}
System.out.println("Total number of shapes:"+ shapes.size());
}



public static void main(String[] args) {


System.out.println("The describe() method:");

System.out.println();
System.out.println("Example rectangles");
ArrayList<Rectangle> rects = new ArrayList<Rectangle>();
rects.add(new Rectangle(2.0, 3.0));
rects.add(new Rectangle(5.0, 5.0));
describe_all(rects);
System.out.println();


ArrayList<Sphere> spheres = new ArrayList<Sphere>();
spheres.add(new Sphere(10.0));
spheres.add(new Sphere(50.0));
spheres.add(new Sphere(0.0));

System.out.println("Example spheres");
describe_all(spheres);
System.out.println();
System.out.println("The supersize() method:");
System.out.println();

ArrayList<Rectangle> double_rects = supersize_list(rects);
describe_all(double_rects);
System.out.println();

ArrayList<Sphere> double_spheres = supersize_list(spheres);
describe_all(double_spheres);

}


}


How can I implement supersize_list method that it takes supersize method from both rectangle and sphere and outputs like

Rectangle[width=4.0, height=6.0]
Rectangle[width=10.0, height=10.0]
Total number of shapes: 2

Sphere[radius=20.0]
Sphere[radius=100.0]
Sphere[radius=0.0]
Total number of shapes: 3


Could you help me with this, please? I greatly appreciate your help!

Answer

The class hierarchy looks inconsistent. For example, you have ThreeDShape extends GeometricShape<ThreeDShape> and TwoDShape extends GeometricShape at the same time, for no obvious reason. It's not fun to write a generic method for these types.

Here's a less-confusing version. (I hope) Note: I choose not to change the size of the shape itself in supersize method, instead let it return a bigger shape while keeping the original unchanged.

1. GeometricShape

/**
 * A geometric shape interface. You can do two things with it.
 * 1. Ask it to describe itself (to stdout);
 * 2. Ask it to return a bigger version of itself (double the size).
 */
public interface GeometricShape<T extends GeometricShape<T>> {
    /**
     * Print a description to STDOUT
     */
    void describe();

    /**
     * Returns a bigger shape.
     * @return Something that's a GeometricShape
     */
    T supersize();
}

2. Shape2D and Rectangle

/**
 * A 2-dimensional shape. 
 * It has area.
 * Its supersize() method should return a Shape2D instance.
 */
public interface Shape2D<T extends Shape2D<T>> extends GeometricShape<T> {

    double area();
}

/**
 * A rectangle.
 */
public final class Rectangle implements Shape2D<Rectangle> {

    private final double width;
    private final double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    @Override
    public String toString() {
        return "Rectangle{" +
                "width=" + width +
                ", height=" + height +
                '}';
    }

    @Override
    public void describe() {
        System.out.println(this);
    }

    @Override
    public Rectangle supersize() {
        return new Rectangle(width*2, height*2);
    }

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

3. Shape3D and Sphere

/**
 * A 3-dimensional shape. 
 * It has volume. 
 * Its supersize() method should return a Shape3D instance.
 */
public interface Shape3D<T extends Shape3D<T>> extends GeometricShape<T> {

    double volume();
}

/**
 * A sphere
 */
public final class Sphere implements Shape3D<Sphere> {
    private final double radius;

    public Sphere(double radius) {
        this.radius = radius;
    }

    @Override
    public String toString() {
        return "Sphere{" +
                "radius=" + radius +
                '}';
    }

    @Override
    public void describe() {
        System.out.println(this);
    }

    @Override
    public Sphere supersize() {
        return new Sphere(radius*2);
    }

    @Override
    public double volume() {
        return 4*Math.PI*Math.pow(radius, 3)/3;
    }
}

Now the generic method that transforms a list

public static <T extends GeometricShape<T>>
List<T> supersize_list(List<T> list) {
    List<T> result = new ArrayList<>();
    for (T shape : list) {
        result.add(shape.supersize());
    }
    return result;
}