Skip to content

Classes and Interfaces

For modeling objects of the same kind you use a class. Different classes may have certain common properties and behaviors which may be generalized in a class that other classes can share. A class represents the set of properties or methods that are common to all objects of one type. class declarations can include the following components:

  • Modifiers: A class can be public or has default access (Refer this for details).
  • Class name: The name should begin with a initial letter (capitalized by convention).
  • Superclass (if any): The name of the class’s parent (superclass), if any, preceded by the keyword extends. A class can only extend (subclass) one parent.
  • Interfaces (if any): A comma-separated list of interfaces implemented by the class, if any, preceded by the keyword implements. A class can implement more than one interface.
  • Body: The class body surrounded by braces, { }.

A specialized class can be defined by extending the generalized class. The specialized class inherit the general Class properties and methods.

Abstract classes

Abstract class can not be instantiated in java. Mostly an abstract class is used to provide a framework for subclasses to expand and enforce abstract methods, and modify or use the methods implemented in abstract class.

public abstract class SimpleAbstractClass {
  public void performAction() {
    // Implementation here
  }
  public abstract void performAnotherAction();
}

Roughly speaking, the abstract keyword is a non-access modifier, used for classes and methods. * Class: An abstract class is a restricted class that cannot be used to create objects (to access it, it must be inherited from another class). * Method: An abstract method can only be used in an abstract class, and it does not have a body. The body is provided by the subclass (inherited from).

Immutable classes

Less or even lack of mutable state contributes to improved scalability and easier reasoning. The Java language sadly does not have good support for the class immutability. But it is possible to build classes which are immutable using a combination of techniques. Above all, all class fields should be final. It's a good start but it doesn't necessarily guarantee immutability.

import java.util.Collection;
public class ImmutableClass {
  private final long id;
  private final String[] arrayOfStrings;
  private final Collection< String > collectionOfString;
}

Proper initialization

if the field is the reference to a collection or array, do not directly assign those fields from constructor arguments, instead make the copies. It will guarantee that it does not change the state of the collection or selection from outside.

public ImmutableClass( final long id, 
          final String[] arrayOfStrings,
          final Collection< String > collectionOfString ) {
  this.id = id;
  this.arrayOfStrings = Arrays.copyOf( arrayOfStrings, arrayOfStrings.length );
  this.collectionOfString = new ArrayList<>( collectionOfString );
}

And lastly, provide the proper accessors (getters). For the collection, the immutable view should be exposed using Collections.unmodifiable... wrappers.

public Collection<String> getCollectionOfString() {
  return Collections.unmodifiableCollection( collectionOfString );
}

With arrays, the only way to ensure true immutability is to return a copy instead of reference to the array. However from a practical standpoint, it may put a lot of pressure on garbage collector, depending on array size.

Anonymous classes

Before Java 8, anonymous classes were used to provide immediate class definitions and instantiations. The purpose of the anonymous classes was to provide a fast and concise way to implement classes as expressions. The following example shows a typical old-fashioned way to define new thread in Java:

public class AnonymousClass{ 
  public static void main(String[]args) {
    new Thread(
      new Runnable() {
        @Override public void run() {
        // Thread Implementation 
        }
      }
    ).start();
  }
}

However, with Java 8, lambdas and functional interfaces have replaced all of such boilerplates, as we see in the following example for the above code:

public class AnonymousClass{ 
  public static void main(String[] args) {
    new Thread( () -> {/* Implementation */}).start();
  }
}

Inheritance

Inheritance is a key concept in object-oriented programming for building class relationships. Together with visibility and accessibility rules, it helps to design extensible and maintainable class hierarchies. Inheritance is implemented using subclassing and the extends keyword following by the parent class. A subclass inherits all of the public and protected members of its parent. In addition, a subclass inherits all private members of the parent class if both reside in the same package. It is the best practice to keep the number of public methods as small as possible.

With the use of the extends keyword, all the properties of the Parent Class (also known as the Super Class or Base Class) are available in the subclass (also known as the Child Class or Derived Class):

public class BaseClass {
  public void baseMethod(){
    System.out.println("Implemented in base class");
  }
}
public class SubClass extends BaseClass {
}
Instances of SubClass have inherited the method baseMethod():
SubClass s = new SubClass();
s.baseMethod(); // prints "Implemented in base class"

A subclass can contains additional content that allows to add additional functionality in the subclass without any change to the base class or the other subclasses:

public class SecondSubclass extends BaseClass {
  public void aubclassMethod() {
    System.out.println("Implemented in SecondSubclass");
  }
}
SecondSubclass s2 = new SecondSubclass();
s2.baseMethod(); // prints "Implemented in base class"
s2.aubclassMethod(); // prints "Implemented in SecondSubclass"

Final classes

The classes with final modifier cannot be extended by other classes. In other words, a final class is a "leaf" class in the inheritance hierarchy.

// This declares a final class
final class MyFinalClass {
  /* some code */
}
// Compilation error: cannot inherit from final MyFinalClass
class MySubClass extends MyFinalClass {
  /* more code */
}

Interfaces

The concept of interfaces shapes the foundations of contract-driven (or contract-based) creation in object-oriented programming. In short, interfaces describe the set of methods (contract) and each class that claims to follow this particular interface must Provide the implementation of these methods: a very basic yet effective concept.

public interface MyInterface {
  void performAction();
}

interfaces in Java can be more complicated than that: they can include nested interfaces, classes, enumerations, annotations and constants.

These are two most important use cases of interfaces: 1. To achieve security - hide certain details and only show the important details of an object (interface). 2. Java does not support "multiple inheritance" (a class can only inherit from one superclass). However, it can be achieved with interfaces, because the class can implement multiple interfaces. Note: To implement multiple interfaces, separate them with a comma.

There are a few restrictions implicitly imposed by interfaces regarding the nested constructs and method definitions, and Java compiler enforces that. First and foremost, every declaration in the interface is public, even if it is not explicitly stated. The declarations below are therefore equivalent:

public void performAction();
void performAction();

Every single interface method is implicitly declared abstract. The declarations below are therefore equivalent:

public abstract void performAction();
public void performAction();
void performAction();

In addition to being public, the constant field definitions are implicitly static and finalised, The declarations below are therefore equivalent:

String CONSTANT = "CONSTANT";
public static final String CONSTANT = "CONSTANT";

to summarize: * Like abstract classes, interfaces cannot be used to create objects * Interface methods do not have a body - the body is provided by the "implement" class * On implementation of an interface, you must override all of its methods * Interface methods are by default abstract and public * Interface attributes are by default public, static and final * An interface cannot contain a constructor (as it cannot be used to create objects)

Marker Interfaces

Marker interfaces are a kind of interfaces that have no defined methods or other nested constructs. The Cloneable interface is an example of marker interface.

public interface Cloneable {
}

Marker interfaces are a useful technique for "attaching" some specific feature to the class . As for Cloneable, for example, the class is marked as being available for cloning but the way it should or could be done is not part of the interface. Another very well-known and widely used marker interface example is Serializable.

Inheritance of interfaces

The implementation could have been simplified a bit more if we took advantage of the fact that a class inherits the interfaces of its base class.

Multiple Implementation

It is quite possible (and common) for a class to implement several interfaces. It must then implement all of the methods defined in each interface. With each implemented interface, the class becomes compatible with the data type defined in it. A class that implements n interfaces is therefore compatible with n + 1 data types (plus their respective superclasses):

  • The parent class from which it was derived (or the class Object, if no extends clause was present)
  • The n interfaces it implements.
interface A {
    public ... methodA();
}
interface B {
    public ... methodA();
}

public class MyClass implements A, B {
    public ... methodA();
    public ... methodB();
}

Derivation of interfaces

Interfaces themselves can also be derived. Similar to a class, the derived interface inherits all method definitions of the basic interface. The implementing class must therefore also implement all methods of all higher-level interfaces:

interface A {
    public ... methodA();
}
interface B extends  A {
    public ... methodA();
}
interface C extends  B {
    public ... methodC();
}

public class MyClass implements C {
    public ... methodA();
    public ... methodB();
        public ... methodC();
}

Functional Interfaces

In above section we highlighted the fact that Java interfaces can only announce methods but are not authorized to provide their implementations. It is no longer true for default methods: an interface may label a method with the default keyword, and provide it with the implementation.

public interface InterfaceWithDefaultMethods {
  void performAction();
  default void performDefaulAction() {
    // Implementation here
  }
}

Defaults methods could be overridden by each interface implementer. Interfaces may also include static methods:

public interface InterfaceWithDefaultMethods {
  static void createAction() {
    // Implementation here
  }
}

One may argue that including an implementation in the interface defeats the purpose of contract-based development, but there are several reasons why such features have been implemented into the Java language and no matter how useful or frustrating they are, they are there to be used.

The functional interface is essentially the interface that contains only one abstract method defined in it. A clear example of this principle is the Runnable interface from the Java standard library:

@FunctionalInterface
public interface Runnable {
  void run();
}

The Java compiler treats functional interfaces differently and convert the lambda function into the functional interface implementation where it makes sense.

public void runMe( final Runnable r ) {
  r.run();
}

To invoke above function in Java 7 and below, the implementation of the Runnable interface should be provided. In Java 8 it is enough to pass run() method implementation using lambda syntax: runMe( () -> System.out.println( "Run!" ) );

Exercise

  • Exercise 1:
    • Write an interface Area with one method that calculate the area of an object. Then write the following three classes that implement the 'Area' interface, and test them by calling the calculate the area method:
      • Paper
      • Car
      • SoccerField
  • Execise 2:
    • With the use of interface write a Helper class for String processing (e.g. with one methods: concatenate two string), and use it in code.
  • Exercise 3:
    • With the use of interface write a factory pattern for different type of vehicles