//************************************************************//
//*******Object Superclass (cont.) - October 7th, 2016*******//
//**********************************************************//

-(for real this time)
-Every class you write in java is part of a class hierarchy/ framework, and ALL classes inherit from a class called "Object"
    -Stored in java.lang.Object; since it's in java.lang, is automatically imported wherever it is

-Object HAS some default methods, but many of them are meant to be overridden (e.g. toString, equals, hashCode, etc.)
    -Java will automatically call toString whenever we try to print an object; how does it know every class has a toString method? Because EVERY class inherits from object, which has a toString method.
        -Clone() method exists, but should NEVER be used (read Joshua Block)
        -Finalize() is helpful in some scenarios (runs code before destroying object), but is only used very rarely, so we won't go over it

-So, we have a class "Animal" that "is-a" Object, and a class "Mammal" which is-a Object

-By default, the .equals() method checks for identity equality, or "ALIASING"- Java checks to see if two objects have the same memory address
-When should a class override this method?
    -When logical equality, or VALUE EQUALITY, is needed (e.g. checking if 2 dates stored in different objects are the same)
    -When classes don't use "instance control" (class checks that there's only one instance of a class (like a singleton))
    -When the equals method for the superclass isn't suitable
    -In this class: ALMOST ALWAYS need to override the .equals().

-More importantly, we need to know not just "when", but HOW to override this
    -SIDE NOTE: ALWAYS use .equals(), unless you're testing for identity equality or working with primitives (otherwise, you can be making a dangerous assumption about how the equality )
    -SIDE NOTE 2: KNOW OPERATOR PRECEDENCE! print("truth value : " + n == q ) will just print "false", since the == operator has precedence and will operate at the end
    -SIDE NOTE 3: When initializing a string with the same text as a pre-existing string, Java will just make the new string an alias of the old one, unless instantiated via String n = new String("coolString");

    -For Comparable interface: we'll get to it later, but don't use it for now

    -In the Java documentation, the .equals(Object n) method should define "an equivalence relationship", which satisfies the properties:
        -Reflexivity: every object is equal to itself
        -Symmetry: A = B is the same as B = C
        -Transitive: A = C and B = C, then A = C
        -Consistent: If A and B don't change, then the same result should be given
        - a.equals(null) should ALWAYS return false (as otherwise, a is null and doesn't have an equals method)

    -e.g. we have a "Person" class, and we follow the recipe of:
        -Check object is not null
        -Check if we're comparing the object to itself
        -Check that the object is an "instanceof" the class we're using (return false if not true)
            -BE AWARE that this will return true if the object is a subclass of the current class
        -Cast the object to the type we want
        -Check the "significant" fields for the other object to determine if the two objects are equivalent

        -We follow this for ALL of our equals methods (for now...)

        -So, we would end up with this for the Person class:

        @Override
        public boolean equals(Object other) {
            if(null == other) {return false; }
            if(this == other) {return true; }
            if(!(other instanceof Person)) {return false; }
                return ((Person)other).getName().equals(this.name);
        }

-Arrays are fixed collections of a fixed size and single type
-ArrayLists are dynamically-allocated, and automatically resizes to fit objects
    -Uses multiple arrays behind the scenes, but we can just use it as a resizeable list

    e.g. ArrayList tasks = new ArrayList();
        tasks.add("Eat");

-The "contains()" method in collections (not just ArrayLists) uses the "equals" method in the stored object to find if an object is in the list; the default implementation rarely works right off the bat for this
    -Note that ArrayList is a "generic" typed collection; by default, we can use it, but it will throw a warning if we don't give the ArrayList a specific type (by default, uses the "Object" type)

-Override equivalence:
    -You want to override a method in a subclass; the method MUST have the EXACT same name, EXACT SAME parameter list, and a covariant return type

-"Covariant return type? Gee, what's that?"
    -Means that it has to return either the same class OR a subclass of the return type as the original class
    -"Return type of equal or lesser hierarchy to the original return type"
        -Practically, returning subclass is rare, but occasionally useful
    -If EVERYTHING ELSE is the same except the return type/method body, then it won't compile (even as overloading)
        -Overloading is only when the parameter list is the only thing that has changed

-Trying to override method in same class as the original method freaks the compiler out (WHICH ONE IS REAL AGGGGHHHHHH), so don't do it
-Make sure to use @Override, as it is easy to OVERLOAD a method instead of actually overriding it

-Static abstract classes: can call the static methods on the class directly w/o instantiation, but really, no one actually does this
    -Can we instantiate a static class? WHO KNOWS! (Taylor didn't know)

-Won't be questions on exceptions (even if they're on the practice test)

-Enums: can do some crazzzzzzy stuff with them (enum constructors, methods, etc.), but for this class, they're just numbers w/ names
    -Values given in order (0, 1, 2, 3, ..., n)
    -If more interested, Oracle has a great tutorial on Enums

(...I got bored and went on a really awful poetry tangent in the margins of a paper I'll probably need later)