Wednesday, April 25, 2012

GIST NOTES 2 - Java Object Orientation



GIST NOTES 2 - Java Object Orientation

[DISCLAIMER: This is solely for non-commercial use. I don't claim ownership of this content. This is a crux of all my readings studies and analysis. Some of them are excerpts from famous books on  the subject. Some of them are my contemplations upon experiments with direct hand coded code samples using IDE or notepad.


I've created this mainly to reduce an entire book into few pages of critical content that we should never forget. Even after years, you don't need to read the entire book again to get back its philosophy. I hope these notes will help you to replay the entire book in your mind once again.]


[JDK 7]

-Getters and Setters are called Accessors and Mutators
-Two most common reasons to use inheritance are: To promote code reuse; To use polymorphism
-any object which can pass more than one IS-A test is considered polymorphic
-All java class objects except for Object objects, pass at least two IS-A test and hence they are all polymorphic
-a reference variables type determines the methods that can be invoked on the object referenced
-java doesn't support multiple inheritance

Deadly Diamond of Death: Multiple inheritance class diagram in the shape of diamond depicting inheritance issue of not being able to decide whether the member is inherited from this class or that. Four classes having inheritance relationship where the class diagram looks like a diamond

-usually you can call only methods available in the type the reference variable is declared as. For example, if the reference variable is declared as Runnable then you can call methods from Runnable interface. You can't call methods from other implemented types say if the class also extends UnicastRemoteObject, you still can't invoke methods from UnicastRemoteObject.

-One exception to this strict decalred type the compiler enforces is "methods from Object class" can be called on any object regardless of its declared type(true, checked in the code!)

Method overriding
-----------------
-subclasses are not allowed to narrow down the visibility or access level of the method being overridden (leads to compilation error)

>> Contract of the super class: Overriding rules

1. argument list of the overridden method should match
2. return type should be same as the one it is declared in the super class or a subtype of it
3. access level should not be restricted
4. access level can be broadened though
5. can throw a new UNCHECKED exception
6. can NOT throw a new CHECKED exception or broaden(more generic) the already thrown checked exception, but can throw a subtype of parent's exception
7. overriding method can narrow(more specific exception) the parent exception
8. final methods cannot be overridden
9. static methods cannot be overriden(just like private methods)
10. a method that was not inherited, obviously CANNOT be overridden(private methods, duh!)
11. using super keyword one can access the parent version of the overridden method in the subclass
12. overriding method can choose not to throw any exception at all(eliminates the parent's exception)
13. calling the overridden method using supertype reference will only call the parent's version and will expect parent's exception be handled (unhandling that exception causes compilation error)

14. change in the return type without changes to argument list is neither 'overriding' nor 'overloading'. overloading requires different argument list than the original method. Such attempts causes compilation error because return type cannot be resolved even at runtime. (you think you are so clever, eh?)

Method overloading
------------------
-overloaded methods MUST change the argument list
-can change return type
-can change access modifier
-can declare newer and broader(more generic) checked exceptions
-overloading can happen in the same class or subclass
-overloading against the inherited method in the subclass still qualifies as proper OVERLOADING but across different classes(actually the inherited method sits in the subclass too)

-if method overloading is based on compatible types(subtype, say first method takes Animal and its overloaded version takes Horse a related type to Animal) when you pass a subtype object through supertype reference, then the method which will be called is the method that takes super type as argument, not the method that takes subtype, though the actual object passed at runtime is of subtype; it is decided during compile time

#That is the choice of which overloaded method to infer in a context is decided at compile time itself.

-polymorphism does not matter when a method is overloaded; it skips the polymorphic special treatment and all
-but polymorphism STILL matters when it comes to method overriding
-compiler is dumb; it doesn't have the patience to check for polymorphic relationships; so don't go invoking subtype methods on a subtype object that is held in supertype reference variable; compiler will recoil at you violently (it would say, "what are you?! you think I've got all day to do your biddings? the nerve!")

Compile time resolution - overloaded method calls
Run time resolution - overridden method calls

downcast(going specific) - casting down the inheritance tree (casting a supertype to a subtype), e.g. (Dog)Animal

-compiler is also dumb enough to not recognize wrong downcasts that are evident EVEN at compile time

e.g. Animal a = new Animal();
Dog d = (Dog) a; //compiler allows this when Dog is a subtype of Animal, though it is a compile time programming error

String name = (String) a; //compilation will fail here because EVEN compiler knows that String is not related to Animal type


upcast(going generic) - doesn't need explicit casting; you can directly assign the subtype(object) to a supertype(reference); super type can be an interface.

upcast   <--> broaden <--> generic
downcast <--> narrow  <--> specific

-a subclass can implement an interface which was already implemented by superclass; in this case, subclass can just declare that it implements the interface but can skip actual implementation of its methods because it was already implemented by the parent

Interface Implementation
------------------------
-a class implementing the interface can narrow(use subtypes) the return-type and declared exception of the methods in the interface
-it can also choose to eliminate the declared exception
-since interfaces can extend more than one interfaces, it is possible for a child interface to directly re-extend a distant ancester who is already in the inheritance tree

e.g.

interface GrandParent{}

interface Parent extends GrandParent{}

interface Child extends Parent{}

interface GrandChild extends Child, Parent, GrandParent{}


Return Type Declaration
-----------------------
-the declared return type of the overriding method should be same or subtype of the declared return type of the method which is being overridden
-subtype for return type is allowed from java 5.0. This is called co-variant returns. Co-variant return type gives compilation error in 1.4 or before

Return Type Values
------------------
-null can be returned for any return type(but not for primitive types)
-char can be returned for int
-int cannot be returned for char [Gives: possible loss of precision error]
-returning any value is not allowed for void return type
-dotted numbers cannot be assigned to float variable
-float can NOT be returned for int return type
-int CAN be returned for float return type
-dotted numbers cannot be assigned to int variable
-all dotted numbers are treated as double
-float values should have 'f' suffixed
-float value cannot be assigned to int variable
-int value can be assigned to a float variable
-dotted number(that is double value) cannot be returned for float return type
-int can be returned for double
-float can be returned for double
-char can be returned for double
-"return void" is illegal syntax
-subclass object can be returned in the place of super return type
-any object that passes IS-A test can be returned safely

Constructors
------------
-you can't make a new object without invoking the constructor
-in addition to that, you can't make a new object without invoking constructors of each of the superclasses in the inheritance hierarchy
-when you say 'new', constructors and initialization blocks are run
-every class(including abstract classes) has a constructor even if it is not explicitly coded
-constructors have no return type and have same name as the class
-if a class doesn't define any constructors, then no-arg constructor will be automatically enabled by the compiler
-if however a class defines at least one constructor with one or more arguments, the no-arg constructor will not be enabled by the compiler and any attempt to instantiate with no-arg constructor results in compilation error
-constructors can be overloaded
-constructors are used to initialize the object and states of its member variables

Constructor Chaining: when you call new operator on a class type, the following happens:-

1. Constructor of the current class is called
2. Then constructor of the immediate superclass is called (automatically using super() if you haven't explicitly called a superclass constructor inside your constructor.
3. Then, next superclass constructor
4. Then next superclass constructor and so on until it reaches Object which is the topmost in java system
5. Instance variables of Object class are initialized with the declared values(explicitly assigned values)
6. Object constructor completes
7. Members of next class below Object get initialized
8. Constructor of the class below Object class completes
9. One step below, next class member variables get initialized with explicit values, and its constructor completes
10. and so on till it reaches the bottom most class in the inheritance hierarchy.
11. Bottom most class member variables are initialized (with explicit values if any)
12. Bottom most class constructor completes

This recursive work is called "constructor chaining".

First: super class constructor runs
Second: current class members get their explicitly assigned values
Third: current class constructor finishes

Rules for constructors
----------------------

1.Constructors can use any access modifiers including private
2.It is legal to have a method with the same name as the class though it doesn't qualify as constructor for it has a return type!
3.Compiler generated default constructor is always no-arg constructor(only when no constructors are explicitly declared)
4.Every constructor as a first statement in it, it should call one of the super class constructor. If not, compiler inserts one(no-arg super constructor only) automatically
5.when the compiler is attempting to insert a call to the super class' default constructor, and if it is not availble, bang!, results in compilation error
6.you can't call intance methods or access instance variables until after the super constructor runs
7.during this() and super() calls, only static methods and static variables can be accessed
8.abstract classes have constructors, but called only when a concrete subclass is instantiated
9.interfaces don't have constructors
10.calling a constructor without new operator (like a method call) is allowed only inside another constructor and nowhere else, mind it
11. class Horse {
void Horse() {}
} //for this class, compiler will insert a default constructor. why? (take a closer look at the code)

12. Props of default constructor: >it has same access modifier as the class, >has no arguments, >includes a call to no-arg super constructor [super();]

13. class Parent {
Parent(String name){}
}

    class Child extends Parent {
//compilation fails here due to the unavailability of default no-arg constructor in the super class
}

14. CONSTRUCTORS ARE NEVER INHERITED; so they can't be overridden, but can be overloaded

15.a constructor instead of calling super constructor, it can call its own constructors this(); but eventully the constructor chaining reaches top level class through the other constructor

16. the first line in a constructor must be either super() or this() or any of their overloaded counterparts [this has to be done either by you or by the compiler; so no exceptions to this rule]

17. using both super() as well as this() in the same constructor gives compile error. because not both of them can be the first line in the constructor ;-)

18. when you explicitly put super() or this() calls, compiler will not insert the default calls to either super() or this(). This allows to write the following erraneous program which some compilers will NOT CATCH (latest compilers DO catch)

class A {

A()
{
this("Hi");
}

A(String greeting)
{
this();
}

}

//7.0 compiler error: recursive constructor invocation
//if compiler didn't catch then it leads to StackOverflowError at runtime

19. overloaded constructors give code-reuse and flexibility in object instantiation


Static Variables and Methods
----------------------------
static method - method that doesn't depend on object state(instance variables) and hence common to the whole class
static - is a scope outside of any object; a perfect place to monitor all objects of a particular class
static members - belong to the class not to any object
static variables - get same default values as the instance variables get

-referencing instance variables or methods from a static context(e.g main method) throws compile error
-static methods cannot be overridden, but can be redefined (what is the difference?); because in proper overriding, subclass can access superclass version of the method using super.method(); but when you redefine static method, the usage of super.method() is not possible because super and this are non-static; that's why this is called "method redefining" instead of calling "overriding"

Redefined static method ===>

1.parent version of the static method is used when parent class name is used for access (Parent.staticMethod())
2.child version is used when child class name is used for access(Child.staticMethod())
3.if an object reference is used to access the static method, then the version available in the declared variable type class is called, though the variable can be holding subclass object. so, reference variable type decides which version of the redefined static method will be called.

e.g. Parent p = new Child();
p.staticMethod(); //calls the static method in Parent class; for overridden non-static methods, Child class method would be called.

Cohesion and Coupling(are subjective concepts)
----------------------------------------------
-high cohesion and loose coupling are good OO designs
-the object for this is easy creation, maintenance and enhancement

coupling: knowledge of the two classes about each other; has to do with how classes interact with each other
loose coupling: two classes know only each others exposed public interfaces
tight coupling: result of poor encapsulation
cohesion: is about how a single class is designed; it is a degree to which a class has a single well focused purpose
high cohesion: class is highly focused on a single purpose; easier to maintain(extensible); more reusable;
low cohesion: has unrelated functionalities in the same class; inflexible to extend; less reusability;


-polymorphism applies to overriding, not to overloading
-object type(not reference variable type) determines which overridden method is used at runtime
-reference type determines which overloaded method will be used at compile time
-reference type determines which redefined static method(no overriding concept for static methods) will be used at compile time

- I think I have a low cohesion!

-a final method cannot be overridden; but if it is a private method, the subclass has no visibility to it; so subclass can put the exact same final method in its own class with different code body(it looks like overriding a final method, but it is not)

-a super class instance variable can be redefined in the subclass; but polymorphism doesn't apply for instance variables; hence, reference type decides which version of the variable will be used at compile time;

-if two overloaded methods one taking parent type argument and other taking child type argument, then always the method taking parent type will be called; the other method will never be used even one tries to call the method with child type object because child type object first is of parent type and it supersedes

-also overloaded var-arg methods are chosen last compared to normal argumented methods; autoboxing can come in to rescue to pick a method in overloaded context



Reference - Kathy Sierra SCJP book

No comments:

Post a Comment