Showing posts with label Java. Show all posts
Showing posts with label Java. Show all posts

Wednesday, August 16, 2017

Covariance & Contravariance in Java

Covariance

If the generic type's subtype relationship is the same as the type parameter T, then the generic type is covariant w.r.t type parameter T.

A is a subtype of B => Generic<A> is a subtype of Generic<B>

In order to define a generic type as covariant, the extends keyword is used which defines the upper bound.

Example:

public class Animal {}
public class Mammal extends Animal {}
public class Cat extends Mammal {}
public class Dog extends Mammal {}


List<Animal> animals = ...
List<Mammal> mammals = ...
List<Cat> cats = ...
List<Dog> dogs = ...

public void someFunc(List<? extends Mammal> mammals)
                                |
                                V
               Here 'extends' defines the upper-bound as Mammal

Covariants are producers 

New items cannot be added to covariants after their construction.
public void someFunc(List<? extends Mammal> mammals) {
    mammals.add(new Cat());  ==> Does not compile
    mammals.add(new Mammal()); ==> Does not compile
}
   
However, one can access the items from covariant types, like so...

public void someFunc(List<? extends Mammal> mammals) {
    Mammal m = mammals.get(0); ==> Compiles
}

Also, someFunc() can only accept List<Mammal> or it's subtypes. 

someFunc(cats); ==> Compiles
someFunc(mammals); ==> Compiles
someFunc(animals); => Does NOT compile


Contravariance

If the generic type's subtype relationship is the opposite of type parameter T, then the generic type is contravariant w.r.t type parameter T.

A is a subtype of B => Generic<B> is a subtype of Generic<A>

In order to define a generic type as contravariant, the super keyword is used which defines the lower bound.

Example:

public void someFunc(List<? super Mammal> animals)
                                |
                                V
               Here 'super' defines the lower-bound as Mammal

Contravariants are consumers 

New items can be added to contravariants after their construction.
public void someFunc(List<? super Mammal> animals) {
    animals.add(new Cat());  ==> Compiles 
                             ==> Adding a Cat compiles as the 
                                 lower bound does NOT apply to the 
                                 constituent type <?> but only to                                    the generic type List<?>
    animals.add(new Mammal()); ==> Compiles
}

However, one cannot access the items from contravariant types, like so...

public void someFunc(List<? super Mammal> animals) {
    Animal a = animals.get(0); ==> Does not compile
}

Also, someFunc() can only accept List<Mammal> or it's super types. 

someFunc(cats); => Does NOT compile
someFunc(dogs); => Does NOT compile
someFunc(mammals); ==> Compiles
someFunc(animals); ==> Compiles
someFunc(objects); ==> Compiles

Wednesday, January 29, 2014

Static factory methods vs Factory pattern

Many of us are familiar with the use of static factory methods for object creation. However, there is quite a bit of confusion as to whether such static factory methods are the same as the factory pattern. Let me attempt to explain the difference between the two here.

Static factory method
A static factory method also known as named constructor is employed either in place of or in addition to traditional constructors for creating objects of the class. 

The advantages of using static factory methods are:

  1. Meaningful names for constructors
  2. Choice of creating new object or returning existing one (Ex: used in singletons)
  3. Can return sub-class objects

Now, static factory method has nothing to do with the Factory pattern. In case of factory pattern, one or more factory methods are defined in a factory class and are used to create objects of multiple possibly unrelated types.

Factory pattern

This pattern hides the object creation logic while providing a well defined interface for object creation.



Friday, January 17, 2014

Double checked locking optimization

Double checked locking aims to improve performance by reducing the need to acquire a lock by checking for the locking criteria first without acquiring the lock. Subsequently, if the lock is acquired, the locking criteria is checked again in order to ensure thread-safe code.

The locking criterion variable needs to be volatile for this to work correctly. If it not volatile, the compiler might optimize access to the variable thus allowing for it to contain an older value when the locking criterion check is made.

This technique is generally used in Singleton definition.

Example
public final class MySingleton implements Cloneable {
  private static volatile MySingleton instance = null;  
  
  private MySingleton() {
  }

  public static MySingleton getInstance() {
     if (instance == null) {
         synchronized(this) {               
               if (instance == null) {
                   instance = new MySingleton();
               }   
           }
     }
      return instance;
  }  
}

Wednesday, January 15, 2014

Singletons in Java

As developers, we all have heard of Singletons. It would not be an exaggeration to state that it is the most popular design pattern. Having said that, what exactly is a Singleton?

A class that can only be instantiated only once is referred to as a Singleton.

In order to design a Singleton class, the following rules should be adopted:
  • Private constructors
    • This prevents creating objects of the class at will and ensures that object creation can only be done by code inside the class
    • It also prevents sub-classing
  • Class should be final
    • This prevents sub-classing
    • It also makes it explicit to the developer that the class cannot be sub-classed
  • Private static Object reference 
    • This is to store the reference to the singleton
    • Should be static
    • Should be private
    • Should be volatile
  • Static method that returns the singleton reference
    • Should be static
    • Should be public
    • Should be synchronized. This is to ensure that the method is thread-safe and a single object is created even in multi-threaded scenarios
    • Create object if not created
    • Return object reference
  • Prevent cloning
    • Implement Cloneable interface and throw the CloneNotSupportedException
    • This prevents cloning of the singleton

Example 1:
public final class MySingleton implements Cloneable {
  private static volatile MySingleton instance = null;
  
  private MySingleton() {
  }

  public static synchronized MySingleton getInstance() {
      if (instance == null) {
          instance = new MySingleton();
      }
      return instance;
  }  

  protected Object clone() throws CloneNotSupportedException {
      throw new CloneNotSupportedException();
  }
}

Approach II
Another approach is to create and assign the object to the static reference variable in the definition itself. Since, static members are initialized only once during class loading, it obviates the need to serialize calls to the static method which returns the singleton.

Example 2:
public final class MySingleton {
  private static MySingleton instance = new MySingleton();
  
  private MySingleton() {
  }

  public static MySingleton getInstance() {
      return instance;
  }  

  protected Object clone() throws CloneNotSupportedException {
      throw new CloneNotSupportedException();
  }
}

References
http://www.javaworld.com/article/2073352/core-java/simply-singleton.html

Friday, January 03, 2014

Java Annotations

What are Annotations?
Annotations are basically syntactic meta-data that can be applied to packages, classes, methods, variables & parameters. 

Purpose
Annotations serve several purposes such as...
  • Provide extra information to the compiler
    • @Override
    • @SuppressWarnings
    • @Deprecated
  • Reduce boiler plate code
    • See project LomBok
    • AOP as in Spring AOP
    • Replacement for marker interfaces
  • Used by frameworks to glue user defined classes together into an application instead of specifying the same in an external XML configuration file
    • Spring
  • Provide information to be used at run-time 

Examples
Annotations can have elements which have values
@Author(name="kirk",  date="1/1/2014")

If annotations have a single element named value, it can be omitted as shown below:
@SuppressWarnings("unchecked")

If annotations have no elements, then the braces can be omitted as well:
@Override





Saturday, December 28, 2013

Java Reflection

Reflection in Java is a feature that makes it possible to inspect classes, interfaces, methods at run-time without knowing the names of classes, interfaces, methods at compile-time. It is also possible to create objects, invoke methods, get/set field values using reflection.

References
Java Reflection Tutorial by Jakob Jenkov
Java Reflection API


Friday, December 27, 2013

Java Collections

A Collection is an object that groups together several objects. The Java Collections framework comprises of...
  • Interfaces
  • Implementations
  • Algorithms

Interfaces & Implementations
The different collection implementations conform to one or more of the generic interfaces shown in the interface hierarchy below...
Interface Hierarchy

Some important Collection interface methods
  • size(), isEmpty()
  • add(), remove(), contains()
  • iterator(), toArray()
  • Bulk operations - addAll(), removeAll(), retainAll(), containsAll()
Bulk operations are so called because they operate on entire collections.

Set interface models the mathematical set abstraction. 
  • Duplicate elements are not allowed
  • No additional methods are defined in this interface
  • equals(), hashcode() methods can be used to compare different sets even though their implementations differ
  • Implementations
    • HastSet - hash table implementation (best performance for most scenarios)
    • TreeSet - Red-Black tree implementation
    • LinkedHashSet - hash table with a linked list running through it
List is an ordered collection
  • Positional access methods - get(), set(), add(), addAll(), remove(), indexOf(), lastIndexOf()
  • ListIterator<>, subList()
  • Implementations
Queue interface models the queue abstraction
  • Typically FIFO type of data structure (except in case of Priority Queues)
  • Two types of methods
    • Methods that throw exception on failure - add(), remove(), element()
    • Methods that return special value on failure - offer(), poll(), peek()
  • Implementations
Deque pronounced as deck is a double-ended queue. I like to think of it as a queue-stack hybrid
  • Permits operations at both ends of the queue
  • Two types of methods
    • Methods that throw exception on failure - addFirst/Last(), removeFirst/Last(), getFirst/Last()
    • Methods that return special value on failure - offerFirst/Last(), pollFirst/Last(), peekFirst/Last()
  • Implements the Queue interface - add(), remove(), offer()... are also available
  • Stack operations supported - push(), pop(), peek()
  • Implementations
Map is an object that maps keys to values. It models the mathematical function abstraction. 
  • No duplicate keys allowed
  • Methods - put(), get(), remove(), containsKey(), containsValue()
  • Collections views - keySet(), values(), entrySet()
  • Implementations
    • HastMap - hash table implementation 
    • TreeMap - Red-Black tree implementation
SortedSet maintains elements in ascending order or according to the order specified by the Comparator provided at creation time. Several additional methods are provided to take advantage of the ordering. Useful for modelling word lists, membership rolls etc

SortedMap maintains the keys in ascending order or according to the order specified by Comparator provided at creation time. Useful for modelling dictionaries, directories etc


Algorithms
Algorithms provide useful computations like sorting and searching over the elements of the collection. They are polymorphic and can be used across the different collection implementations.