Hacks

College Board Classes and Objects

As a part of OOP, classes and objects are fundamentals to the OOP paradigm serving as the building blocks. Essentially, classes are like blueprints and objects are just instances or constructions of that blue print. Classes have a lot of properties and in particular their most useful quality is gathering data and methods into a single place. Methods and data types can either only be called through an instantiation of the class or statically called from the class

Access Modifiers

Access modifiers indicate the level of access different classes/objects have over other classes and objects. Of the four most common types, there are:

  • private: only the object/class at hand can modify the object/class itself and cannot be called by sub classes or any other class.
  • protected: subclasses can modify/use methods and data from its super classes
  • default: default is the setting which enables the entire package (usually sub-classes) to access methods or data.
  • public: any thing can call the methods or access the data of public variables/functions

Constructor

Constructors are the most important part of classes/objects since they dictate what happens to the object on the instantiation of the class. They usually ether take in parameters and set them data fields using those parameters, or run internal logic to set those parameters.

Modifiers

Modifiers are special attachments to functions that dictate the use of that function. For instance static only enables the class to call upon functions/data. Finals prevents any other change from occurring at all.

Getters/Setters

Getters and setters are usually reserved for private fields and help access references of certain classes to both as implied get information and set information.

Inheritance, Polymorphism, Abstraction, and Encapsulation

Probably the most important part about classes and objects are the 4 pillars of OOP. Inheritance is the passing down of methods and data from a super class to sub classes. This helps organize objects as "like" each other. We see this particular in this weeks hacks with GenericType and Object classes. Polymorphism is the ability for multiple classes to have the same name implying same output for the same reason but perhaps a different implementation. Abstraction is the broader overview of classes where we want to limit the scope of certain classes as much as possible so that only the most important of their implementation is visible and usable. Finally, encapsulation is the "wrapping" of classes into single objects/classes.

Generic Classes

/* This is wrapper class...
 Objective would be to push more functionality into this Class to enforce consistent definition
 */
public abstract class Generics {
	public final String masterType = "Generic";
	private String type;	// extender should define their data type

	// generic enumerated interface
	public interface KeyTypes {
		String name();
	}
	protected abstract KeyTypes getKey();  	// this method helps force usage of KeyTypes

	// getter
	public String getMasterType() {
		return masterType;
	}

	// getter
	public String getType() {
		return type;
	}

	// setter
	public void setType(String type) {
		this.type = type;
	}
	
	// this method is used to establish key order
	public abstract String toString();

	// static print method used by extended classes
	public static void print(Generics[] objs) {
		// print 'Object' properties
		System.out.println(objs.getClass() + " " + objs.length);

		// print 'Generics' properties
		if (objs.length > 0) {
			Generics obj = objs[0];	// Look at properties of 1st element
			System.out.println(
					obj.getMasterType() + ": " + 
					obj.getType() +
					" listed by " +
					obj.getKey());
		}

		// print "Generics: Objects'
		for(Object o : objs)	// observe that type is Opaque
			System.out.println(o);

		System.out.println();
	}
}
public class Owner extends Generics {

    public static KeyTypes key = KeyType.title;
	public static void setOrder(KeyTypes key) { Owner.key = key; }
	public enum KeyType implements KeyTypes {title, name, age, year, trusted}

	private final String name;
	private final int age;
	private final int year;
    
    // True if used, false if new
    private final boolean trusted;

	public Owner(String name, int year, int age, boolean trusted)
	{
		super.setType("Owner");
		this.name = name;
		this.age = age;
		this.year = year;
        this.trusted = trusted; 
	}

	@Override
	protected KeyTypes getKey() { return Owner.key; }
	

	@Override
	public String toString()
	{
		String output="";
		if (KeyType.name.equals(this.getKey())) {
			output += this.name;
		} else if (KeyType.age.equals(this.getKey())) {
			output += "00" + this.age;
			output = output.substring(output.length() - 2);
		} else if (KeyType.year.equals(this.getKey())) {
			output += this.year; 
		} else if (KeyType.trusted.equals(this.getKey())) {
			output += this.trusted;
		} else {
			output += super.getType() + ": " + this.name + ", " + this.year + ", " + this.age + ", " + this.trusted;
		}
		return output;
		
	}

	// Test data initializer
	public static Owner[] owners() {
		return new Owner[]{
				new Owner("Don Trannn", 17, 2023, false),
				new Owner("Bailey Say", 17, 2023, true),
				new Owner("Evan Sanches", 17, 2023, true)		
            };
	}
	

	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		Owner[] objs = owners();

		// print with title
		Owner.setOrder(KeyType.title);
		Owner.print(objs);

		// print name only
		Owner.setOrder(KeyType.name);
		Owner.print(objs);
	}

}
Owner.main(null);
class [LREPL.$JShell$20B$Owner; 3
Generic: Owner listed by title
Owner: Don Trannn, 17, 2023, false
Owner: Bailey Say, 17, 2023, true
Owner: Evan Sanches, 17, 2023, true

class [LREPL.$JShell$20B$Owner; 3
Generic: Owner listed by name
Don Trannn
Bailey Say
Evan Sanches

/**
 *  Implementation of a Double Linked List;  forward and backward links point to adjacent Nodes.
 *
 */

 public class LinkedList<T>
 {
     private T data;
     private LinkedList<T> prevNode, nextNode;
 
     /**
      *  Constructs a new element
      *
      * @param  data, data of object
      * @param  node, previous node
      */
     public LinkedList(T data, LinkedList<T> node)
     {
         this.setData(data);
         this.setPrevNode(node);
         this.setNextNode(null);
     }
 
     /**
      *  Clone an object,
      *
      * @param  node  object to clone
      */
     public LinkedList(LinkedList<T> node)
     {
         this.setData(node.data);
         this.setPrevNode(node.prevNode);
         this.setNextNode(node.nextNode);
     }
 
     /**
      *  Setter for T data in DoubleLinkedNode object
      *
      * @param  data, update data of object
      */
     public void setData(T data)
     {
         this.data = data;
     }
 
     /**
      *  Returns T data for this element
      *
      * @return  data associated with object
      */
     public T getData()
     {
         return this.data;
     }
 
     /**
      *  Setter for prevNode in DoubleLinkedNode object
      *
      * @param node, prevNode to current Object
      */
     public void setPrevNode(LinkedList<T> node)
     {
         this.prevNode = node;
     }
 
     /**
      *  Setter for nextNode in DoubleLinkedNode object
      *
      * @param node, nextNode to current Object
      */
     public void setNextNode(LinkedList<T> node)
     {
         this.nextNode = node;
     }
 
 
     /**
      *  Returns reference to previous object in list
      *
      * @return  the previous object in the list
      */
     public LinkedList<T> getPrevious()
     {
         return this.prevNode;
     }
 
     /**
      *  Returns reference to next object in list
      *
      * @return  the next object in the list
      */
     public LinkedList<T> getNext()
     {
         return this.nextNode;
     }
 
 }
import java.util.Iterator;

/**
 * Queue Iterator
 *
 * 1. "has a" current reference in Queue
 * 2. supports iterable required methods for next that returns a generic T Object
 */
class QueueIterator<T> implements Iterator<T> {
    LinkedList<T> current;  // current element in iteration

    // QueueIterator is pointed to the head of the list for iteration
    public QueueIterator(LinkedList<T> head) {
        current = head;
    }

    // hasNext informs if next element exists
    public boolean hasNext() {
        return current != null;
    }

    // next returns data object and advances to next position in queue
    public T next() {
        T data = current.getData();
        current = current.getNext();
        return data;
    }
}

/**
 * Queue: custom implementation
 * @author     John Mortensen
 *
 * 1. Uses custom LinkedList of Generic type T
 * 2. Implements Iterable
 * 3. "has a" LinkedList for head and tail
 */
public class Queue<T> implements Iterable<T> {
    LinkedList<T> head = null, tail = null;

    /**
     *  Add a new object at the end of the Queue,
     *
     * @param  data,  is the data to be inserted in the Queue.
     */
    public void add(T data) {
        // add new object to end of Queue
        LinkedList<T> tail = new LinkedList<>(data, null);

        if (this.head == null)  // initial condition
            this.head = this.tail = tail;
        else {  // nodes in queue
            this.tail.setNextNode(tail); // current tail points to new tail
            this.tail = tail;  // update tail
        }
    }

    /**
     *  Returns the data of head.
     *
     * @return  data, the dequeued data
     */
    public T delete() {
        T data = this.peek();
        if (this.tail != null) { // initial condition
            this.head = this.head.getNext(); // current tail points to new tail
            if (this.head != null) {
                this.head.setPrevNode(tail);
            }
        }
        return data;
    }

    /**
     *  Returns the data of head.
     *
     * @return  this.head.getData(), the head data in Queue.
     */
    public T peek() {
        return this.head.getData();
    }

    /**
     *  Returns the head object.
     *
     * @return  this.head, the head object in Queue.
     */
    public LinkedList<T> getHead() {
        return this.head;
    }

    /**
     *  Returns the tail object.
     *
     * @return  this.tail, the last object in Queue
     */
    public LinkedList<T> getTail() {
        return this.tail;
    }

    /**
     *  Returns the iterator object.
     *
     * @return  this, instance of object
     */
    public Iterator<T> iterator() {
        return new QueueIterator<>(this.head);
    }
}
/**
 * Queue Manager
 * 1. "has a" Queue
 * 2. support management of Queue tasks (aka: titling, adding a list, printing)
 */
class QueueManager<T> {
    // queue data
    private final String name; // name of queue
    private int count = 0; // number of objects in queue
    public final Queue<T> queue = new Queue<>(); // queue object

    /**
     *  Queue constructor
     *  Title with empty queue
     */
    public QueueManager(String name) {
        this.name = name;
    }

    /**
     *  Queue constructor
     *  Title with series of Arrays of Objects
     */
    public QueueManager(String name, T[]... seriesOfObjects) {
        this.name = name;
        this.addList(seriesOfObjects);
    }

    /**
     * Add a list of objects to queue
     */
    public void addList(T[]... seriesOfObjects) {  //accepts multiple generic T lists
        for (T[] objects: seriesOfObjects)
            for (T data : objects) {
                this.queue.add(data);
                this.count++;
            }
    }

    /**
     * Print any array objects from queue
     */
    public void printQueue() {
        System.out.println(this.name + " count: " + count);
        System.out.print(this.name + " data: ");
        for (T data : queue)
            System.out.print(data + " ");
        System.out.println();
    }
}
/**
 * Driver Class
 * Tests queue with string, integers, and mixes of Classes and types
 */
class QueueTester {
    public static void main(String[] args)
    {

        // Create iterable Queue of NCS Generics
        Owner.setOrder(Owner.KeyType.name);
        
        // Illustrates use of a series of repeating arguments
        QueueManager qGenerics = new QueueManager("My Generics", Owner.owners());
        qGenerics.printQueue();
        
        qGenerics.queue.add(new Owner("Nathan Shih", 18, 2023, false));
        
        qGenerics.printQueue();
        
        qGenerics.queue.delete();
        
        qGenerics.printQueue();


    }
}
QueueTester.main(null);
My Generics count: 3
My Generics data: Don Trannn Bailey Say Evan Sanches 
My Generics count: 3
My Generics data: Don Trannn Bailey Say Evan Sanches Nathan Shih 
My Generics count: 3
My Generics data: Bailey Say Evan Sanches Nathan Shih 

Example of Linked List, Queues, Stacks

import java.util.Iterator;

public class LinkedList<T> {
    private T data;
    private LinkedList<T> prevNode, nextNode;

    // Create new node with T data and reference to previous LinkedList<T> node
    public LinkedList(T data, LinkedList<T> node) {
        setData(data);
        setPrevNode(node);
        setNextNode(null);
    }

    // Create new node using copy
    public LinkedList(LinkedList<T> node) {
        setData(node.data);
        setPrevNode(node.prevNode);
        setNextNode(node.nextNode);
    }

    // Getters
    public T getData() {
        return this.data;
    }

    public LinkedList<T> getPrev() {
        return this.prevNode;
    }

    public LinkedList<T> getNext() {
        return this.nextNode;
    }

    // Setters
    public void setData(T data) {
        this.data = data;
    }

    public void setPrevNode(LinkedList<T> node) {
        this.prevNode = node;
    }

    public void setNextNode(LinkedList<T> node) {
        this.nextNode = node;
    }
}

/**
 * Queue Iterator
 *
 * 1. "has a" current reference in Queue
 * 2. supports iterable required methods for next that returns a generic T Object
 */
class QueueIterator<T> implements Iterator<T> {
    LinkedList<T> current;  // current element in iteration

    // QueueIterator is pointed to the head of the list for iteration
    public QueueIterator(LinkedList<T> head) {
        current = head;
    }

    // hasNext informs if next element exists
    public boolean hasNext() {
        return current != null;
    }

    // next returns data object and advances to next position in queue
    public T next() {
        T data = current.getData();
        current = current.getNext();
        return data;
    }
}

/**
 * Queue: custom implementation
 * @author     John Mortensen
 *
 * 1. Uses custom LinkedList of Generic type T
 * 2. Implements Iterable
 * 3. "has a" LinkedList for head and tail
 */
public class Queue<T> implements Iterable<T> {
    LinkedList<T> head = null, tail = null;

    /**
     *  Add a new object at the end of the Queue,
     *
     * @param  data,  is the data to be inserted in the Queue.
     */
    public void add(T data) {
        // add new object to end of Queue
        LinkedList<T> tail = new LinkedList<>(data, null);

        if (this.head == null)  // initial condition
            this.head = this.tail = tail;
        else {  // nodes in queue
            this.tail.setNextNode(tail); // current tail points to new tail
            this.tail = tail;  // update tail
        }
    }

    /**
     *  Returns the data of head.
     *
     * @return  data, the dequeued data
     */
    public T delete() {
        try {
            T data = this.peek();
            if (this.tail != null) { // initial condition
                this.head = this.head.getNext(); // current tail points to new tail
                if (this.head != null) {
                    this.head.setPrevNode(tail);
                }
            }
            return data;
        } catch (Exception E) {
            return null;
        }
        
    }

    /**
     *  Returns the data of head.
     *
     * @return  this.head.getData(), the head data in Queue.
     */
    public T peek() {
        try {
            return this.head.getData();
        } catch (Exception E) {
            return null;
        }
    }

    /**
     *  Returns the head object.
     *
     * @return  this.head, the head object in Queue.
     */
    public LinkedList<T> getHead() {
        return this.head;
    }

    /**
     *  Returns the tail object.
     *
     * @return  this.tail, the last object in Queue
     */
    public LinkedList<T> getTail() {
        return this.tail;
    }

    /**
     *  Returns the iterator object.
     *
     * @return  this, instance of object
     */
    public Iterator<T> iterator() {
        return new QueueIterator<>(this.head);
    }
}
/**
 * Queue Manager
 * 1. "has a" Queue
 * 2. support management of Queue tasks (aka: titling, adding a list, printing)
 */
class QueueManager<T> {
    // queue data
    private final String name; // name of queue
    private int count = 0; // number of objects in queue
    public final Queue<T> queue = new Queue<>(); // queue object

    /**
     *  Queue constructor
     *  Title with empty queue
     */
    public QueueManager(String name) {
        this.name = name;
    }

    /**
     *  Queue constructor
     *  Title with series of Arrays of Objects
     */
    public QueueManager(String name, T[]... seriesOfObjects) {
        this.name = name;
        this.addList(seriesOfObjects);
    }

    /**
     * Add a list of objects to queue
     */
    public void addList(T[]... seriesOfObjects) {  //accepts multiple generic T lists
        for (T[] objects: seriesOfObjects)
            for (T data : objects) {
                this.queue.add(data);
                this.count++;
            }
    }

    // Challenge 1 
    public T delete() {
        count--;
        return this.queue.delete();
    }

    /**
     * Print any array objects from queue
     */
    public void printQueue() {
        System.out.println(this.name + " count: " + count);
        System.out.print(this.name + " data: ");
        for (T data : queue)
            System.out.print(data + " ");
        System.out.println();
    }
}

/**
 * Driver Class
 * Tests queue with string, integers, and mixes of Classes and types
 */
class QueueTester {
    public static void main(String[] args)
    {
        // Create iterable Queue of Words
        Object[] words = new String[] { "seven", "slimy", "snakes", "sallying", "slowly", "slithered", "southward"};
        QueueManager qWords = new QueueManager("Words", words );
        qWords.printQueue();

        while (qWords.delete() != null) {
            qWords.printQueue();
        }
    }
}
QueueTester.main(null);
Words count: 7
Words data: seven slimy snakes sallying slowly slithered southward 
Words count: 6
Words data: slimy snakes sallying slowly slithered southward 
Words count: 5
Words data: snakes sallying slowly slithered southward 
Words count: 4
Words data: sallying slowly slithered southward 
Words count: 3
Words data: slowly slithered southward 
Words count: 2
Words data: slithered southward 
Words count: 1
Words data: southward 
Words count: 0
Words data: