Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- import java.util.*;
- // Shape.java
- // We decide we want to build an app that contains
- // different Shapes. What is common is all objects
- // are shapes, but what is different is the shapes
- // are different, each have their own attributes
- // (to be modeled as fields) and behaviour (meaning,
- // different method implementations)
- // we declare the class as abstract thus forbidding
- // to create objects of this type, which means,
- // we intend this class to be extended. Inheritance
- // ("extension") would mean that we will derive another
- // class from this base class, which means we will copy
- // (almost) everything and then add/change something, as
- // we desire
- abstract class Shape implements Comparable<Shape> {
- // common attribute for all the classes
- private String type;
- // unique to Shape as constructors are not inheritted,
- // every class has its own constructor(s), but can
- // call/use constructors of base/parent/superclasses
- public Shape(String type){
- this.type = type;
- }
- // we implement this as an example how we can make
- // the class sortable, i.e. we don't write a sorting
- // algorithm, because it is already done in Collections.sort
- // but we need to provide a comparator which would
- // explain how we want to sort a new type, in this case,
- // Shape, and we say inside that when comparing Shapes
- // we simply compare their types, which are strings for
- // which compareTo is already implemented in Java,
- // i.e. we build on already existing functionality
- public int compareTo(Shape shape){
- return type.compareTo(shape.type);
- }
- public String getType(){
- return type;
- }
- // we could return 0, but this is just a dummy value
- // which does not make sense and is there just as a
- // "filler" which we try to avoid (eg. constructors
- // requiring values rather than having 0, "empty" etc
- // thus we declare the method as abstract meaning
- // derived classes will have to implement it or also
- // will stay abstract
- public abstract double getArea();
- }
- // Square.java
- class Square extends Shape {
- // we provide an atrribute/field which
- // is unique to Square
- private double length;
- // we provide a unique constructor,
- // which in the first line calls appropriate
- // constructor from a base class, using keyword
- // "super" and then supplying the parameters,
- // it has to be the first line of the constructor;
- // note that we can also call multiple constructors
- // from the same class to avoid duplication, using
- // keyword "this"
- public Square(double length){
- super("Square");
- this.length = length;
- }
- // override/implement an abstract method
- // with a specific algorithm and thus the class
- // becomes concrete, i.e. stops being abstract
- // and since it is not declared abstract and has not
- // abstract methods, an object can be created
- public double getArea(){
- return length*length;
- }
- }
- // Circle.java
- class Circle extends Shape {
- // we do the same thing for Circle,
- // simply the implementation/details/specifics
- // are different, i.e. base/parent/superclasses
- // specifies what is common/stable, and derived/child/subclass
- // specifies what is changing/specific/different
- // and we have a single base class and multiple
- // derived classes thus constituting a hierarchy
- private double radius;
- public Circle(double radius){
- super("Circle");
- this.radius = radius;
- }
- public double getArea(){
- return 2 * Math.PI * radius;
- }
- }
- // Main.java
- // this is an alternative way of sorting, i.e. instead
- // of providing a stable/main version of comparing Shapes
- // we can decide there are multiple ways, and thus split
- // the functionality among multiple Comparators, each of
- // which implements Comparator<T> and does it differently,
- // and when we sort we supply the required implementation,
- // one of many, i.e. when we find a changing/unstable aspect,
- // we delegate it to a separate class thus following the
- // idea of modular programming, i.e. like Lego, changing one
- // block/brick into another without the need to change the
- // whole project
- class AreaComparator implements Comparator<Shape> {
- public int compare(Shape s1, Shape s2){
- if(s1.getArea() < s2.getArea())
- return -1;
- else if(s1.getArea() == s2.getArea())
- return 0;
- else
- return 1;
- }
- }
- public class Main {
- public static void main(String [] args){
- // note that a reference/pointer of the parent/ancestor
- // can reference to any child/successor
- // in the same hierarchy (also true for interfaces)
- // because any child IS also an instance of a parent,
- // can have more functionality or can change the existing
- // implementations by overriding already inherited methods
- // but it cannot delete them - it means some implementation
- // will always be available if we call it, compared to otherwise,
- // when we would call the method from a child which was added
- // and would not be available in a parent, what then?
- //Shape shape = new Shape("Square");
- //Shape square = new Square(2);
- //Shape circle = new Circle(1);
- // and this would constitute as an example of
- // (subtype) polymorphism, i.e. we write .getArea()
- // the interface is the same, but implementations are
- // different and one is picked based on which object
- // we have created; i.e. we use the reference of the
- // type Shape, but because it points to the instances
- // of Square/Circle, the corresponding implementation
- // from Square/Circle are used, thus resulting in
- // polymorphic behaviour - same line has "many forms",
- // i.e. looks the same but acts differently`
- //System.out.println(shape.getArea());
- //System.out.println(square.getArea());
- //System.out.println(circle.getArea());
- // and now we demonstrate how we use Shape as
- // an abstraction, leaving details for derived classes
- // avoiding switch or if/else statements and
- // the need to know the exhaustive list of all the
- // subtypes; subtypes can be added later by writing
- // new classes, and the code does not need to know/change -
- // it will be handled by polymorphic behaviour
- ArrayList<Shape> list = new ArrayList<Shape>();
- // we generate some data to put into our list
- Random r = new Random();
- for(int i = 0; i < 10; ++i){
- int temp = r.nextInt();
- if(temp%2 == 0){
- list.add(new Circle(Math.abs(r.nextInt()%10)));
- } else {
- list.add(new Square(Math.abs(r.nextInt()%10)));
- }
- }
- // and now we just sort, i.e. we use a comparator,
- // and the comparator calls getArea, and getArea used
- // depends on the specific object in question,
- // i.e. Square computes area differently than Circle;
- // and we don't care, we don't need to know, it is the
- // responsibility of the Square/Circle, and here we
- // care about something else, i.e. we need to sort and
- // we don't need to know the details, we just say -
- // sort the list using AreaComparator, list is responsible
- // for storage, and the comparator is responsible for
- // order, and the .sort is responsible for the sorting
- // algorithm; those can change independently, and we don'temp
- // care because one does not need to know about the otherwise
- // - this is the essence of the principle of information hiding
- // which is the essence of what quality in the code means
- Collections.sort(list, new AreaComparator());
- // and now we print, again, getType is the same of all
- // and getArea is different depending on the object, but
- // the list does not know, thus we witness polymorphism
- // again, in the full glory
- for(int i = 0; i < list.size(); ++i){
- System.out.print(list.get(i).getType());
- System.out.println(" " + list.get(i).getArea());
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement