sveska

OCP Review 2.1 - Interfaces

Interfaces

  • Can declare any number of methods
  • Can be a marker interface (empty)
  • Can extend another interface

  • The methods of an interface are implicitly abstract and public.
  • The vairables of an interface are implicitly public, static, and final.
  • Because the methods in an interface are implicitly public, if you try to assign a weaker access to the implemented method in a class, it won’t compile.

interface Marker {
}

interface A extends Marker {   
}

Can’t

  • a class can’t extend from an Interface
  • can’t create an instance of an interface
  • can’t contain instance / static blocks of code

Instance of

Marker m = new Marker() {   // this is an anonymous class implementing Marker
};

System.out.println(m instanceof Marker);

We’re instantiating an Object


What does this print?

interface Marker {
}

interface A extends Marker {   
}

// ...

A a = new A() {
};
System.out.println(a instanceof Marker);
System.out.println(a instanceof A);

true and true


Always public abstract

  • we can use any form: the compiler treats them as always public abstract
  • in the interface definition we can omit public abstract. In the class implementation: NO
public interface Decompress {
    public abstract void decompress();
    abstract void decompress();
    public void decompress();
    void decompress();
}

Interface default implementations

interface Compress {
    void compress();
}

interface CompressExtension extends Compress {
    @Override
    default void compress() {
        System.out.println("I'm compressing");
    }
}

class ZipCompressor implements CompressExtension {

}

Static members in an interface

interface I1 {
    static void sm() {
        System.out.println("Static method");
    }
    
    default void m1() {
        System.out.println("I1::m1");
    }
}

interface I2 extends I1 {
    default void m1() {
        I1.sm();        // we need to use the Interface I1 as sm is defined there
        System.out.println("I2::m1");
    }
}

class A implements I2, I1 {
    @Override
    public void m1() {
        I1.sm();
    }

}

Default implementations useful?

  • to move common code from subclasses to small, focused interfaces instead of a big-ass abstract parent class
  • f.i. interface FilePersistable: we can persist compressed files, or music, photos, etc.
  • the ability to be saved on file is a trait of many different classes

Problem 1

  • two different (unrelated) interfaces provide the same default
class ZipCompressor implements CompressExtension, AnotherCompressExtension {    // Error
}

interface CompressExtension extends Compress {
    @Override
    default void compress() {
        System.out.println("I'm compressing in CompressExtension");
    }
}

interface AnotherCompressExtension extends Compress {
    @Override
    default void compress() {
        System.out.println("I'm compressing in AnotherCompressExtension");
    }
}


Problem 1, simplified

interface I1 {
    default void m1() {
        System.out.println("I1::m1");
    }
}

interface I2 {
    default void m1() {
        System.out.println("I2::m1");
    }
}

class A implements I1, I2 {     // COMPILATION ERROR
    
}

Problem 1, solved

interface I1 {
    default void m1() {
        System.out.println("I1::m1");
    }
}

interface I2 extends I1 {
    default void m1() {
        System.out.println("I2::m1");   // this is used
    }
}

class A implements I1, I2 {

}

Problem 2

  • one interface provides a default impl
  • another interface extends from it and provides a different default impl
class ZipCompressor implements AnotherCompressExtension {
}

interface CompressExtension extends Compress {
    @Override
    default void compress() {
        System.out.println("I'm compressing in CompressExtension");
    }
}

interface AnotherCompressExtension extends CompressExtension {
    @Override
    default void compress() {
        System.out.println("I'm compressing in AnotherCompressExtension");
    }
}

ZipCompressor zc = new ZipCompressor();
zc.compress();                       // I'm compressing in AnotherCompressExtension
((CompressExtension)zc).compress();  // I'm compressing in AnotherCompressExtension

Uses of interfaces & default impl

  • To quickly create mocks
  • To let you compile and print warning while running