Tabla de contenidos
El objetivo general de esta práctica es repasar el uso
de la herencia y el polimorfismo en Java. La herencia la basaremos en
el uso de abstract
y extends
, con los que
crearemos clases genéricas y específicas siguiendo una determinada
jerarquía. Las clases específicas implementarán los métodos abstractos
definidos en las genéricas y sobreescribirán otros métodos que
permitirán demostrar el polimorfismo. La jerarquía de clases de este
ejercicio estará compuesta por la clase Figure
de la que
derivarán las clases Circle
, Triangle
y
Quadrilateral
. Además, de la clase
Quadrilateral
derivará la clase
Rectangle
.
Una vez implementadas todas las clases, se creará un contenedor con objetos de diferentes clases, sobre el cual realizaremos operaciones de forma sencilla aprovechando la potencia del polimorfismo en Java.
La clase Figure
representa una figura
genérica que se materializará posteriormente en una específica
(Circle
, Triangle
,
Rectangle
). En Java, esto se representa mediante las
clases abstractas que se declaran mediante la palabra reservada
abstract
.
El primer ejercicio de la práctica consiste en
definir la siguiente clase abstracta Figure
. Puedes
descargar su código en Figure.java
.
public abstract class Figure { // Name of the figure private String name; // Constructor of the figure public Figure(String name) { } // Calculates the area of a figure abstract public double area(); // Indicates if the figure is regular or not abstract public boolean isRegular(); // Gets the name of the figure protected String getName() { } // Sets the name of the figure protected void setName(String name) { } }
Figure
.Programa el constructor de la clase
Figure
y los métodos get y set
correspondientes.
Circle
.La clase Circle
deriva de
Figure
y debes implementarla teniendo en cuenta esta
situación. Realiza las siguientes acciones sobre la clase
Circle:
Declara la clase Circle
para que
herede (extends
) de la clase
Figure
.
Implementa el constructor. Dicho constructor
debe recibir tres parámetros, un nombre (name
) de
tipo String
, un centro (center
) de
tipo Point
, y un radio (radius
) de
tipo int
. Utiliza la clase Point.java
y llama al constructor de la clase base Figure
mediante la referencia super
en la implementación
del constructor de la clase Circle
.
Implementa el método area()
que
devuelva el área del círculo. Haz uso de Math.PI
para el cálculo del área.
Implementa el método isRegular()
que devuelva true
si la figura es regular y
false
en caso contrario. Ten en cuenta que un
círculo siempre es una figura regular.
Sobreescribe el método toString()
para que devuelva el nombre de la figura, su centro y su
radio.
Implementa los métodos get y set necesarios en esta clase.
Triangle
.La clase Triangle
también deriva de
Figure
. Realiza las siguientes acciones sobre la
clase Triangle.java
Declara la clase Triangle
para
que herede de la clase Figure
.
Implementa el constructor, el cual debe
recibir cuatro parámetros, un nombre (name
) de
tipo String
, y tres puntos (vertex1
,
vertex2
y vertex3
) de tipo
Point
representando los tres vértices del
triángulo. Ten en cuenta que debes llamar al constructor de la
clase base Figure
mediante la referencia
super
.
Implementa el método area()
que
calcule el área del triángulo. Utiliza la siguiente fórmula
area = 0.5*|Ax(By-Cy)+Bx(Cy-Ay)+Cx(Ay-By)|
donde
x
e y
representan las coordenadas en
el eje x y en el eje y de los tres vértices del triángulo
(A,B,C), y |x|
el valor absoluto de
x
.
Implementa el método isRegular()
que indique si el triángulo es regular, es decir si sus tres
lados son iguales.
Sobreescribe el método toString()
para que devuelva el nombre de la figura, y sus
vértices.
Implementa los métodos get y set necesarios en esta clase.
Quadrilateral
.La clase Quadrilateral
es otra clase
que deriva de Figure
. Sin embargo, la clase
Quadrilateral
es una clase abstracta ya que no es
posible implementar el método que calcula el área sin conocer el
cuadrilátero concreto. Realiza las siguientes acciones sobre la
clase Quadrilateral
.
Declara la clase Quadrilateral
para que herede de la clase Figure
.
Implementa el constructor, el cual debe
recibir cinco parámetros, un nombre (name
) de
tipo String
, y cuatro puntos
(vertex1
, vertex2
,
vertex3
y vertex4
) de tipo
Point
representando los cuatro vértices del
cuadrilátero. Ten en cuenta que debes llamar al constructor de
la clase base Figure
mediante la referencia
super
.
Implementa el método isRegular()
que indique si el cuadrilatero es regular, es decir si es un
rectángulo o un cuadrado. Para ello puedes llamar desde el
método isRegular()
al método privado
checkRectangle()
que a su vez invoca
al método privado scalarProduct()
.
El código de ambos métodos se indica al final de este
apartado. ¿Qué es lo que hacen estos métodos?
Sobreescribe el método toString()
para que devuelva el nombre de la figura, y sus
vértices.
Implementa los métodos get y set necesarios en esta clase.
/** * Indicates if the quadrilateral is a rectangle or square by * comparing sizes and diagonals and calculating the scalar product * */ private boolean checkRectangle(Point v1, Point v2, Point v3, Point v4){ Point auxVertex = v1.nearest(new Point[]{v2,v3,v4}); if (auxVertex.equals(v2)){ return v1.distance(v3) == v2.distance(v4) && v1.distance(v4) == v2.distance(v3) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v3,v4})); } else if (auxVertex.equals(v3)){ return v1.distance(v2) == v3.distance(v4) && v1.distance(v4) == v3.distance(v2) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v2,v4})); } else if (auxVertex.equals(v4)){ return v1.distance(v2) == v4.distance(v3) && v1.distance(v3) == v4.distance(v2) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v2,v3})); } else { return false; } } private boolean scalarProduct(Point p1, Point p2, Point p3){ return (p3.getY()-p1.getY())*(p2.getY()-p1.getY()) + (p3.getX()-p1.getX())*(p2.getX()-p1.getX()) == 0; }
Rectangle
.La clase Rectangle
hereda de
Quadrilateral
. Realiza las siguientes acciones sobre
la clase Rectangle
Declara la clase Rectangle
para
que herede de la clase Quadrilateral
.
Implementa el constructor para que reciba
cinco parámetros, un nombre (name
) de tipo
String
, y cuatro puntos (vertex1
,
vertex2
, vertex3
y
vertex4
) de tipo Point
representando
los cuatro vértices del rectángulo. Ten en cuenta que debes
llamar al constructor de la clase base
Quadrilateral
mediante la referencia
super
. Comprueba como parte del constructor que
el rectángulo efectivamente es un cuadrilátero regular y en
caso contrario lanza una excepción (recomendamos que revises
la documentación sobre excepciones)
Implementa el método area()
que
calcule el área del rectángulo. Para ello debes conocer la
base y la altura. En un rectángulo, dado un vértice
V1
, la distancia con los dos vértices más
cercanos definen la base y la altura del rectángulo.
Sobreescribe el método toString()
para particularizarlo al rectángulo.
La clase FiguresSet
contiene un
ArrayList de Figuras y permitirá calcular el área total de un
conjunto de figuras. Esta clase tiene el siguiente aspecto:
import java.util.ArrayList; public class FiguresSet { // Array of figures private ArrayList<Figure> figures; // Constructor of FiguresSet public FiguresSet() { } // Calculates the total area of the figures public double totalArea() { } // figures to String public String toString() { } // Adds a new figure to the FigureSet public void addFigure(Figure f) { } // Main program public static void main(String args[]) throws Exception { } }
El constructor de la clase debe crear un objeto de
la clase ArrayList
sobre el que se irán añadiendo las
distintas figuras. El método addFigure()
permite
añadir un objeto Figure
en el contenedor. El método
totalArea()
es el que irá sumando las areas de las
figuras que contiene el array y devolverá la suma total de
ellas. El método toString()
devuelve un
String
con la información de todas las figuras.
total con algún mensaje descriptivo de las figuras.
Implementa el constructor de la clase.
Programa en método addFigure (Figure
f)
que permite añadir un objeto Figure
al
contenedor.
Programa en método totalArea()
que permite calcular el area total de las figuras del
contenedor.
Programa en método toString()
que
permite devolver la información sobre las figuras incluidas en
el contenedor.
main
de la clase
FiguresSet
Crea el programa principal que tiene que realizar lo siguiente:
Crear un objeto de la clase
FiguresSet
.
Crear un círculo.
Crea un triángulo.
Crea un rectángulo.
Añade el círculo, el triangulo y el rectángulo al objecto de
la clase FigureSet
.
Imprime el resultado del calculo del area total de las figuras y también la información sobre las figuras.
public abstract class Figure { // Name of the figure private String name; // Constructor of the figure with a name public Figure (String name){ this.name = name; } // Calculates the area of a figure public abstract double area(); // Indicates if the figure is regular or not public abstract boolean isRegular(); // Gets the name of the figure protected String getName(){ return this.name; } // Sets the name of the figure protected void setName(String name){ this.name = name; } }
public class Point { private double x; private double y; public Point(double x, double y) { this.x = x; this.y = y; } /** * Returns the string representation of the point. * */ public String toString() { return "(" + x + ", " + y + ")"; } /** * Returns the distance to the origin. * */ public double distance() { Point origin = new Point(0.0, 0.0); return distance(origin); } /** * Returns the x coordinate of the point. * */ public double getX() { return x; } /** * Returns the y coordinate of the point. * */ public double getY() { return y; } /** * Returns the distance to another point. * */ public double distance(Point anotherPoint) { return Math.sqrt(Math.pow(x - anotherPoint.getX(), 2) + Math.pow(y - anotherPoint.getY(), 2)); } /** * Returns the quadrant in which the point is. * */ public int quadrant() { if (x > 0.0 && y > 0.0) { return 1; } else if (x < 0.0 && y > 0.0) { return 2; } else if (x < 0.0 && y < 0.0) { return 3; } else if (x > 0.0 && y < 0.0) { return 4; } else { // (x==0.0 || y==0.0) return 0; } } /** * Returns the nearest point of the array in the parameter, or * null if array is empty. * */ public Point nearest(Point[] otherPoints) { Point nearestPoint = null; double minDistance = Double.MAX_VALUE; double currentDistance; for (int i=0; i<otherPoints.length; i++) { currentDistance = this.distance(otherPoints[i]); if (currentDistance <= minDistance) { minDistance = currentDistance; nearestPoint = otherPoints[i]; } } return nearestPoint; } }
public class Circle extends Figure { public static final double PI = Math.PI; private Point center; private int radius; // Constructor of the circle public Circle (String name, Point center, int radius){ super(name); this.center = center; this.radius = radius; } // Calculates the area of the circle public double area() { return PI*radius*radius; } // The circle is always a regular figure public boolean isRegular() { return true; } // Circle to String public String toString() { return ("Circle: " + getName() + "; Radius: " + getRadius() + "; Center" + getCenter().toString()); } // Gets the center of the circle private Point getCenter(){ return this.center; } // Sets the center of the circle private void setCenter(Point center){ this.center = center; } // Gets the radius of the circle private int getRadius(){ return this.radius; } // Sets the radius of the circle private void setRadius(int radius){ this.radius = radius; } }
public class Triangle extends Figure{ // Vertexes of the triangle private Point vertex1; private Point vertex2; private Point vertex3; // Constructor of the triangle public Triangle(String name, Point vertex1, Point vertex2, Point vertex3) { super(name); this.vertex1 = vertex1; this.vertex2 = vertex2; this.vertex3 = vertex3; } // Calculates the area of the triangle public double area() { return 0.5 * Math.abs(vertex1.getX() * (vertex2.getY() - vertex3.getY()) + vertex2.getX() * (vertex3.getY() - vertex1.getY()) + vertex3.getX() * (vertex1.getY() - vertex2.getY())); } // Indicates if the triangle is regular (equilateral triangle) or not public boolean isRegular(){ return (vertex1.distance(vertex2) == vertex2.distance(vertex3)) && (vertex1.distance(vertex2) == vertex3.distance(vertex1)); } // Triangle to String public String toString() { return ("Triangle: " + getName() + "; Vertexes: " + getVertex1().toString() + getVertex2().toString() + getVertex3().toString()); } // Gets vertex1 of the triangle private Point getVertex1() { return vertex1; } // Gets vertex2 of the triangle private Point getVertex2() { return vertex2; } // Gets vertex3 of the triangle private Point getVertex3() { return vertex3; } // Sets vertex1 of the triangle private void setVertex1(Point vertex1) { this.vertex1 = vertex1; } // Sets vertex2 of the triangle private void setVertex2(Point vertex2) { this.vertex2 = vertex2; } // Sets vertex3 of the triangle private void setVertex3(Point vertex3) { this.vertex3 = vertex3; } }
public abstract class Quadrilateral extends Figure { // Vertexes of the quadrilateral private Point vertex1; private Point vertex2; private Point vertex3; private Point vertex4; // Constructor of the quadrilateral public Quadrilateral(String name, Point vertex1, Point vertex2, Point vertex3, Point vertex4) { super(name); this.vertex1 = vertex1; this.vertex2 = vertex2; this.vertex3 = vertex3; this.vertex4 = vertex4; } // Calculates the area of the quadrilateral public abstract double area(); // Indicates if the quadrilateral is regular. public boolean isRegular(){ return checkRectangle(vertex1, vertex2, vertex3, vertex4); } /** * Indicates if the quadrilateral is a rectangle or square by * comparing sizes and diagonals and calculating the scalar product. * */ private boolean checkRectangle(Point v1, Point v2, Point v3, Point v4){ Point auxVertex = v1.nearest(new Point[]{v2,v3,v4}); if (auxVertex.equals(v2)){ return v1.distance(v3) == v2.distance(v4) && v1.distance(v4) == v2.distance(v3) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v3,v4})); } else if (auxVertex.equals(v3)){ return v1.distance(v2) == v3.distance(v4) && v1.distance(v4) == v3.distance(v2) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v2,v4})); } else if (auxVertex.equals(v4)){ return v1.distance(v2) == v4.distance(v3) && v1.distance(v3) == v4.distance(v2) && scalarProduct(v1,auxVertex,v1.nearest(new Point[]{v2,v3})); } else { return false; } } private boolean scalarProduct(Point p1, Point p2, Point p3){ return (p3.getY()-p1.getY())*(p2.getY()-p1.getY()) + (p3.getX()-p1.getX())*(p2.getX()-p1.getX()) == 0; } // Quadrilateral to String public String toString(){ return (getName() + "; Vertexes: " + getVertex1().toString() + getVertex2().toString() + getVertex3().toString() + getVertex4().toString()); } // Gets vertex1 of the quadrilateral protected Point getVertex1(){ return vertex1; } // Gets vertex2 of the quadrilateral protected Point getVertex2(){ return vertex2; } // Gets vertex3 of the quadrilateral protected Point getVertex3(){ return vertex3; } // Gets vertex4 of the quadrilateral protected Point getVertex4(){ return vertex4; } // Sets vertex1 of the quadrilateral protected void setVertex1(Point vertex1){ this.vertex1 = vertex1; } // Sets vertex2 of the quadrilateral protected void setVertex2(Point vertex2){ this.vertex2 = vertex2; } // Sets vertex3 of the quadrilateral protected void setVertex3(Point vertex3){ this.vertex3 = vertex3; } // Sets vertex4 of the quadrilateral protected void setVertex4(Point vertex4){ this.vertex4 = vertex4; } }
public class Rectangle extends Quadrilateral { // Constructor of the rectangle public Rectangle(String name, Point vertex1, Point vertex2, Point vertex3, Point vertex4) throws BadFigureException { super(name,vertex1,vertex2,vertex3,vertex4); if (isRegular() == false){ throw new BadFigureException("The vertexes are incompatible with" + " a rectangle."); } } // Calculates the area of the rectangle (base times the height) public double area() { Point[] vertices234 = new Point[] {getVertex2(), getVertex3(), getVertex4()}; Point auxVertex = getVertex1().nearest(vertices234); Point[] otherVertices = null; if (auxVertex.equals(getVertex2())) { otherVertices = new Point[] {getVertex3(), getVertex4()}; } else if (auxVertex.equals(getVertex3())) { otherVertices = new Point[] {getVertex2(), getVertex4()}; } else if (auxVertex.equals(getVertex4())) { otherVertices = new Point[] {getVertex2(), getVertex3()}; } double base = getVertex1().distance(auxVertex); double height = auxVertex.distance(auxVertex.nearest(otherVertices)); return base * height; } // Rectangle to String public String toString() { return ("Rectangle: " + super.toString()); } }
import java.util.ArrayList; public class FiguresSet { // Array of figures private ArrayList<Figure> figures; // Constructor of FiguresSet public FiguresSet() { figures = new ArrayList<Figure>(); } // Calculates the total area of the figures public double totalArea() { double totalArea = 0; for (int i=0; i<figures.size(); i++){ totalArea = totalArea + figures.get(i).area(); } return totalArea; } // figures to String public String toString() { String s = new String(); for (int i=0; i<figures.size(); i++){ s = s + figures.get(i).toString() + "\n"; } return s; } // Adds a new figure to the FigureSet public void addFigure(Figure f) { figures.add(f); } // Main program public static void main(String args[]) throws Exception { FiguresSet figures = new FiguresSet(); Circle c = new Circle("Circle1", new Point(1, 1), 4); Triangle t = new Triangle("Triangle1", new Point(1, 1), new Point(3, 1), new Point(2, 3)); figures.addFigure(c); figures.addFigure(t); try { Rectangle r = new Rectangle("Rectangle1", new Point(1, 4), new Point(4, 1), new Point(1, 1), new Point(4, 4)); figures.addFigure(r); } catch (BadFigureException e) { System.out.println("Error: " + e.getMessage()); } System.out.println(figures.toString()); System.out.println("Total area: " + figures.totalArea()); } }
Una de las situaciones en que las interfaces resultan útiles en Java es aquella en que se dispone de varias implementaciones alternativas para proporcionar una misma funcionalidad. En ese caso, es deseable que el resto del programa pueda utilizar cualquiera de estas implementaciones de forma homogénea, sin importar de qué implementación concreta se trate.
En este ejercicio trabajaremos con una situación de este tipo: dispondremos de varias implementaciones distintas para calcular el número pi. Para ello, definimos la siguiente interfaz:
import java.math.BigDecimal; /** * Interface to be implemented by classes that compute the number pi. * */ public interface PiProvider { /** * Computes and returns the value of the number pi. Implementations * may decide the precision with which they compute the value. * * @return The number pi, with the precision each implementation * decides. * */ BigDecimal computePi(); }
Dado que algunas implementaciones son capaces de calcular pi con
la precisión que se les solicite, se define también la siguiente
interfaz, que hereda de PiProvider
y añade varios
métodos nuevos que permiten gestionar la precisión del cálculo,
entendida como el número de dígitos de pi:
import java.math.BigDecimal; /** * Interface to be implemented by classes that compute the number pi. * Classes that implement this interface can provide the value of pi * with the requested precision, understood as the number of exact * digits of the computed value. * */ public interface AdvancedPiProvider extends PiProvider { /** * Sets the desired precision. * * @param precision The desired precision (number of digits). * * @throws PrecisionException if the desired precision is * negative, zero or bigger than the maximum precision * this class can provide. * */ void setPrecision(int precision) throws PrecisionException; /** * Returns the current value of precision. * * @return The current value of precision (number of digits). * */ int getPrecision(); /** * Returns the maximum precision with which this provider is able * to generate the value of pi. * * @return The maximum precision available from this provider, or * Integer.MAX_VALUE if the provider can provide an * arbitrarily big precision. * */ int getMaximumPrecision(); }
Como se puede observar, el método setPrecision
puede
lanzar una excepción si el valor de precisión no es válido (menor
que uno o superior a la máxima precisión a la que la
implementación concreta dé soporte. A continuación se muestra el
código de esta excepción:
public class PrecisionException extends Exception { public PrecisionException(int precision) { super("Unsupported precision: " + precision); } }
Programa y prueba una clase llamada PiSimple
que
implemente la interfaz PiProvider
y devuelva
siempre el valor de pi como 3,14.
import java.math.BigDecimal; public class PiSimple implements PiProvider { public BigDecimal computePi() { return new BigDecimal("3.14"); } }
Programa y prueba una clase llamada PiFromMath
que
implemente la interfaz PiProvider
y devuelva el
valor Math.PI
convertido a BigDecimal
.
import java.math.BigDecimal; public class PiFromMath implements PiProvider { public BigDecimal computePi() { return new BigDecimal(Math.PI); } }
Programa y prueba una clase llamada PiFromBBP
que
implemente la interfaz AdvancedPiProvider
,
basándote en el código de la clase PiCalc
vista en
una práctica previa. Sólo tendrás que hacer algunas pequeñas
adaptaciones a este código para que implemente la interfaz:
import java.math.BigDecimal; import java.math.MathContext; public class PiCalc { private int numDigits; private MathContext mc; public PiCalc(int numDigits) { this.numDigits = numDigits; mc = new MathContext(numDigits); } public BigDecimal compute() { BigDecimal pi = new BigDecimal(0); BigDecimal limit = new BigDecimal(1).movePointLeft(numDigits); boolean stop = false; for (int k = 0; !stop; k++) { BigDecimal piK = piFunction(k); pi = pi.add(piK); if (piK.compareTo(limit) < 0) { stop = true; } } return pi.round(mc); } private BigDecimal piFunction(int k) { int k8 = 8 * k; BigDecimal val1 = new BigDecimal(4); val1 = val1.divide(new BigDecimal(k8 + 1), mc); BigDecimal val2 = new BigDecimal(-2); val2 = val2.divide(new BigDecimal(k8 + 4), mc); BigDecimal val3 = new BigDecimal(-1); val3 = val3.divide(new BigDecimal(k8 + 5), mc); BigDecimal val4 = new BigDecimal(-1); val4 = val4.divide(new BigDecimal(k8 + 6), mc); BigDecimal val = val1; val = val.add(val2); val = val.add(val3); val = val.add(val4); BigDecimal multiplier = new BigDecimal(16); multiplier = multiplier.pow(k); BigDecimal one = new BigDecimal(1); multiplier = one.divide(multiplier, mc); val = val.multiply(multiplier); return val; } public static void main(String[] args) { if (args.length != 1) { System.err.println("One command-line argument expected: number of " + "digits."); } else { PiCalc piCalc = new PiCalc(Integer.parseInt(args[0])); System.out.println(piCalc.compute()); } } }
Al contrario que en el código de partida, el constructor de la nueva clase no debe recibir parámetros. Establecerá un valor por defecto de precisión de 30 dígitos.
import java.math.BigDecimal; import java.math.MathContext; public class PiFromBBP implements AdvancedPiProvider { private int numDigits; private MathContext mc; public PiFromBBP() { setPrecisionInternal(30); } public void setPrecision(int numDigits) throws PrecisionException { if (numDigits < 1) { throw new PrecisionException(numDigits); } setPrecisionInternal(numDigits); } public int getPrecision() { return numDigits; } public int getMaximumPrecision() { return Integer.MAX_VALUE; } public BigDecimal computePi() { BigDecimal pi = new BigDecimal(0); BigDecimal limit = new BigDecimal(1).movePointLeft(numDigits); boolean stop = false; for (int k = 0; !stop; k++) { BigDecimal piK = piFunction(k); pi = pi.add(piK); if (piK.compareTo(limit) < 0) { stop = true; } } return pi.round(mc); } private void setPrecisionInternal(int numDigits) { this.numDigits = numDigits; mc = new MathContext(numDigits); } private BigDecimal piFunction(int k) { int k8 = 8 * k; BigDecimal val1 = new BigDecimal(4); val1 = val1.divide(new BigDecimal(k8 + 1), mc); BigDecimal val2 = new BigDecimal(-2); val2 = val2.divide(new BigDecimal(k8 + 4), mc); BigDecimal val3 = new BigDecimal(-1); val3 = val3.divide(new BigDecimal(k8 + 5), mc); BigDecimal val4 = new BigDecimal(-1); val4 = val4.divide(new BigDecimal(k8 + 6), mc); BigDecimal val = val1; val = val.add(val2); val = val.add(val3); val = val.add(val4); BigDecimal multiplier = new BigDecimal(16); multiplier = multiplier.pow(k); BigDecimal one = new BigDecimal(1); multiplier = one.divide(multiplier, mc); val = val.multiply(multiplier); return val; } }
Programa y prueba una clase llamada PiStored
que
almacene un valor precalculado de pi como cadena de
texto. Cuando se le solicite computar el valor, simplemente
devolverá la subcadena pertinente (según la precisión que se
haya establecido). La precisión máxima vendrá determinada por el
número de caracteres de la cadena de texto que almacenes
internamente.
El constructor no debe recibir parámetros. Establecerá un valor por defecto de precisión de 30 dígitos.
Puedes declarar el valor de pi con la siguiente cadena:
private static final String PI = "3.14159265358979323846264338327950" + "2884197169399375105820974944592307" + "8164062862089986280348253421170679";
Para quedarte con un trozo de la cadena, puedes utilizar el
método substring
de la clase
String
.
import java.math.BigDecimal; public class PiStored implements AdvancedPiProvider { private int numDigits; private static final String PI = "3.14159265358979323846264338327950" + "2884197169399375105820974944592307" + "8164062862089986280348253421170679"; public PiStored() { setPrecisionInternal(getMaximumPrecision()); } public void setPrecision(int numDigits) throws PrecisionException { if (numDigits < 1 || numDigits > getMaximumPrecision()) { throw new PrecisionException(numDigits); } setPrecisionInternal(numDigits); } public int getPrecision() { return numDigits; } public int getMaximumPrecision() { return PI.length() - 1; } public BigDecimal computePi() { int length; if (numDigits == 1) { length = 1; } else { length = 1 + numDigits; } BigDecimal pi = new BigDecimal(PI.substring(0, length)); return pi; } private void setPrecisionInternal(int numDigits) { this.numDigits = numDigits; } }
Programa y prueba una clase llamada Circle
que:
BigDecimal
.
area
que recibe un
objeto que implemente la interfaz PiProvider
y
devuelve al área del círculo calculada mediante el valor de pi
que proporcione dicho objeto.
Utiliza adecuadamente el polimorfismo para que esta clase sea
capaz de utilizar cualquier implementación de
PiProvider
, previendo que en el futuro podrían
crearse nuevas implementaciones distintas a las que ya has
desarrollado. Prueba el código utilizando instancias de cada una
de las implementaciones que has hecho.
import java.math.BigDecimal; public class Circle { private BigDecimal radius; public Circle(BigDecimal radius) { this.radius = radius; } public BigDecimal area(PiProvider piProvider) { return radius.multiply(radius).multiply(piProvider.computePi()); } }
import java.math.BigDecimal; public class TestPi { public static void main(String[] args) throws PrecisionException { PiProvider piSimple = new PiSimple(); System.out.println("Pi simple:\t" + piSimple.computePi()); PiProvider piFromMath = new PiFromMath(); System.out.println("Pi from math:\t" + piFromMath.computePi()); AdvancedPiProvider piFromBBP = new PiFromBBP(); piFromBBP.setPrecision(40); if (piFromBBP.getPrecision() != 40) { System.out.println("Wrong precision in PiFromBBP"); } System.out.println("Pi from BBP:\t" + piFromBBP.computePi()); AdvancedPiProvider piStored = new PiStored(); piStored.setPrecision(40); if (piStored.getPrecision() != 40) { System.out.println("Wrong precision in PiStored"); } System.out.println("Pi stored:\t" + piStored.computePi()); Circle circle = new Circle(new BigDecimal(2)); System.out.println("\nArea simple:\t" + circle.area(piSimple)); System.out.println("Area from math:\t" + circle.area(piFromMath)); System.out.println("Area from BBP:\t" + circle.area(piFromBBP)); System.out.println("Area stored:\t" + circle.area(piStored)); } }