/* 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();
	}
}
/*
 * Animal class extends Generics and defines abstract methods
 */
public class FRQ extends Generics {
	// Class data
	public static KeyTypes key = KeyType.title;  // static initializer
	public static void setOrder(KeyTypes key) { FRQ.key = key; }
	public enum KeyType implements KeyTypes {title, year, difficulty, passRate,}

	// Instance data
	private final int year;
	private final String difficulty;
	private final double passRate;

	/* constructor
	 *
	 */
	public FRQ(int year, String difficulty, double passRate)
	{
		super.setType("FRQ");
		this.year = year;
		this.difficulty = difficulty;
		this.passRate = passRate; 
	}

	/* 'Generics' requires getKey to help enforce KeyTypes usage */
	@Override
	protected KeyTypes getKey() { return FRQ.key; }
	
	/* 'Generics' requires toString override
	 * toString provides data based off of Static Key setting
	 */
	@Override
	public String toString()
	{
		String output="";
		if (KeyType.year.equals(this.getKey())) {
			output += this.year;
			// output = output.substring(output.length() - 2);
		} else if (KeyType.difficulty.equals(this.getKey())) {
			output += this.difficulty;
		} else if (KeyType.passRate.equals(this.getKey())) {
			output += this.passRate;
		} else {
			output += super.getType() + ": " + this.year + ", " + this.difficulty +  ", " + this.passRate;
		}
		return output;
		
	}

	// Test data initializer
	public static FRQ[] FRQs() {
		return new FRQ[]{
				new FRQ(2012, "medium", 63.5),
				new FRQ(2014, "easy/medium", 60.8),
				new FRQ(2016, "medium",  64.4),
				new FRQ(2019, "easy", 70),

		};
	}
	
	/* main to test Animal class
	 * 
	 */
	public static void main(String[] args)
	{
		// Inheritance Hierarchy
		FRQ[] objs = FRQs();

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

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

}
FRQ.main(null);
class [LREPL.$JShell$13F$FRQ; 4
Generic: FRQ listed by title
FRQ: 2012, medium, 63.5
FRQ: 2014, easy/medium, 60.8
FRQ: 2016, medium, 64.4
FRQ: 2019, easy, 70.0

class [LREPL.$JShell$13F$FRQ; 4
Generic: FRQ listed by year
2012
2014
2016
2019