Getting started with Java LanguageInheritanceStreamsExceptions and exception handlingCollectionsLambda ExpressionsGenericsFile I/OArraysInterfacesMapsStringsInputStreams and OutputStreamsDefault MethodsClasses and ObjectsBasic Control StructuresConcurrent Programming (Threads)Console I/OSingletonsVisibility (controlling access to members of a class)Regular ExpressionsAutoboxingDocumenting Java CodeExecutor, ExecutorService and Thread poolsObject Class Methods and ConstructorJAXBPrimitive Data TypesNetworkingOptionalEnumsHttpURLConnectionAnnotationsAudioDate ClassCalendar and its SubclassesNashorn JavaScript engineJava Native InterfaceRemote Method Invocation (RMI)Iterator and IterableOperatorsAssertingScannerProperties ClassPreferencesReflection APIConstructorsByteBufferSerializationJSON in JavaRandom Number GenerationRecursionPolymorphismStringBuilderReference Data TypesBit ManipulationJava AgentsEncapsulationType ConversionBigIntegerBigDecimalRSA EncryptionVarargs (Variable Argument)ThreadLocalLogging (java.util.logging)Using the static keywordDisassembling and DecompilingResources (on classpath)log4j / log4j2JVM FlagsOracle Official Code StandardCharacter encodingJava Memory ManagementImmutable ObjectsObject CloningAlternative CollectionsListsBufferedWriterLocalTimeSetsComparable and ComparatorJVM Tool InterfaceNested and Inner ClassesApache Commons LangGetters and SettersThe ClasspathBytecode ModificationXML Parsing using the JAXP APIsReference TypesLocalization and InternationalizationJAX-WSXML XPath EvaluationJava Performance TuningParallel programming with Fork/Join frameworkCommon Java PitfallsNon-Access ModifiersJava Compiler - 'javac'XJCProcessInstalling Java (Standard Edition)Command line Argument ProcessingDates and Time (java.time.*)Fluent InterfaceXOM - XML Object ModelJust in Time (JIT) compilerFTP (File Transfer Protocol)Java Native AccessModulesJava Pitfalls - Exception usageJava Pitfalls - Language syntaxServiceLoaderClassloadersObject ReferencesJava Pitfalls - Performance IssuesCreating Images ProgrammaticallyAppletsNIO - NetworkingNew File I/OSecure objectsJava Pitfalls - Threads and ConcurrencySplitting a string into fixed length partsJava Pitfalls - Nulls and NullPointerExceptionSecurityManagerJNDIsuper keywordThe java.util.Objects ClassThe Java Command - 'java' and 'javaw'Atomic TypesJava Floating Point OperationsConverting to and from Stringssun.misc.UnsafeJava Memory ModelJava deploymentJava plugin system implementationsQueues and DequesRuntime CommandsNumberFormatSecurity & CryptographyJava Virtual Machine (JVM)Unit TestingJavaBeanExpressionsLiteralsJava SE 8 FeaturesJava SE 7 FeaturesPackagesCurrency and MoneyConcurrent CollectionsUsing ThreadPoolExecutor in MultiThreaded applications.Java Editions, Versions, Releases and DistributionsDynamic Method DispatchJMXSecurity & CryptographyGenerating Java CodeJShellBenchmarksCollection Factory MethodsMulti-Release JAR FilesStack-Walking APITreeMap and TreeSetSocketsJava SocketsUsing Other Scripting Languages in JavaFunctional InterfacesList vs SET2D Graphics in JavaClass - Java ReflectionDequeue InterfaceEnum MapEnumSet classLocal Inner ClassJava Print ServiceImmutable ClassString TokenizerFileUpload to AWSAppDynamics and TIBCO BusinessWorks Instrumentation for Easy IntegrationReaders and WritersHashtableEnum starting with numberSortedMapWeakHashMapLinkedHashMapStringBufferChoosing CollectionsC++ ComparisonCompletableFuture

Default Methods

Other topics

Remarks:

Default methods

  • Can be used within an interface, to introduce a behaviour without forcing existing subclasses to implement it.
  • Can be overridden by subclasses or by a sub-interface.
  • Are not allowed to override methods in java.lang.Object class.
  • If a class implementing more than one interface, inherits default methods with identical method signatures from each of the intefaces, then it must override and provide its own interface as if they were not default methods (as part of resolving multiple inheritance).
  • Although are intended to introduce a behaviour without breaking existing implementations, existing subclasses with a static method with same method signature as the newly introduced default method will still be broken. However this is true even in case of introducing an instance method in a superclass.



Static methods

  • Can be used within an interface, primarily intended to be used as a utility method for default methods.
  • Cannot be overridden by subclasses or by a sub-interface (is hidden to them). However as is the case with static methods even now, each class or interface can have its own.
  • Are not allowed to override instance methods in java.lang.Object class (as is presently the case for subclasses as well).



Below is a table summarizing the interaction between sub-class and super-class.

-SUPER_CLASS-INSTANCE-METHODSUPER_CLASS-STATIC-METHOD
SUB_CLASS-INSTANCE-METHODoverridesgenerates-compiletime-error
SUB_CLASS-STATIC-METHODgenerates-compiletime-errorhides



Below is a table summarizing the interaction between interface and implementing-class.

-INTERFACE-DEFAULT-METHODINTERFACE-STATIC-METHOD
IMPL_CLASS-INSTANCE-METHODoverrideshides
IMPL_CLASS-STATIC-METHODgenerates-compiletime-errorhides

References :

  • http://www.journaldev.com/2752/java-8-interface-changes-static-method-default-method
  • https://docs.oracle.com/javase/tutorial/java/IandI/override.html

Basic usage of default methods

/**
 * Interface with default method
 */
public interface Printable {
    default void printString() {
        System.out.println( "default implementation" );
    }
}

/**
 * Class which falls back to default implementation of {@link #printString()}
 */
public class WithDefault
    implements Printable
{
}

/**
 * Custom implementation of {@link #printString()}
 */
public class OverrideDefault
    implements Printable {
    @Override
    public void printString() {
        System.out.println( "overridden implementation" );
    }
}

The following statements

    new WithDefault().printString();
    new OverrideDefault().printString();

Will produce this output:

default implementation
overridden implementation

Accessing other interface methods within default method

You can as well access other interface methods from within your default method.

public interface Summable {
    int getA();

    int getB();

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {
    @Override
    public int getA() {
        return 1;
    }

    @Override
    public int getB() {
        return 2;
    }
}

The following statement will print 3:

System.out.println(new Sum().calculateSum());

Default methods could be used along with interface static methods as well:

public interface Summable {
    static int getA() {
        return 1;
    }

    static int getB() {
        return 2;
    }

    default int calculateSum() {
        return getA() + getB();
    }
}

public class Sum implements Summable {}

The following statement will also print 3:

System.out.println(new Sum().calculateSum());

Accessing overridden default methods from implementing class

In classes, super.foo() will look in superclasses only. If you want to call a default implementation from a superinterface, you need to qualify super with the interface name: Fooable.super.foo().

public interface Fooable {
    default int foo() {return 3;}
}

public class A extends Object implements Fooable {
    @Override
    public int foo() {
        //return super.foo() + 1; //error: no method foo() in java.lang.Object
        return Fooable.super.foo() + 1; //okay, returns 4
    }
}

Why use Default Methods?

The simple answer is that it allows you to evolve an existing interface without breaking existing implementations.

For example, you have Swim interface that you published 20 years ago.

public interface Swim {
    void backStroke();
}

We did a great job, our interface is very popular, there are many implementation on that around the world and you don't have control over their source code.

public class FooSwimmer implements Swim {
    public void backStroke() {
         System.out.println("Do backstroke");
    }
}

After 20 years, you've decided to add new functionality to the interface, but it looks like our interface is frozen because it will break existing implementations.

Luckily Java 8 introduces brand new feature called Default method.

We can now add new method to the Swim interface.

public interface Swim {
    void backStroke();
    default void sideStroke() {
        System.out.println("Default sidestroke implementation. Can be overridden");
    }
}

Now all existing implementations of our interface can still work. But most importantly they can implement the newly added method in their own time.

One of the biggest reasons for this change, and one of its biggest uses, is in the Java Collections framework. Oracle could not add a foreach method to the existing Iterable interface without breaking all existing code which implemented Iterable. By adding default methods, existing Iterable implementation will inherit the default implementation.

Class, Abstract class and Interface method precedence

Implementations in classes, including abstract declarations, take precedence over all interface defaults.

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
    public void backStroke() {
        System.out.println("AbstractSwimmer.backStroke");
    }
}

public class FooSwimmer extends AbstractSwimmer {
}

The following statement

new FooSwimmer().backStroke();

Will produce

AbstractSwimmer.backStroke

public interface Swim {
    default void backStroke() {
        System.out.println("Swim.backStroke");
    }
}

public abstract class AbstractSwimmer implements Swim {
}

public class FooSwimmer extends AbstractSwimmer {
    public void backStroke() {
        System.out.println("FooSwimmer.backStroke");
    }
}

The following statement

new FooSwimmer().backStroke();

Will produce

FooSwimmer.backStroke

Default method multiple inheritance collision

Consider next example:

public interface A {
    default void foo() { System.out.println("A.foo"); }
}

public interface B {
    default void foo() { System.out.println("B.foo"); }
}

Here are two interfaces declaring default method foo with the same signature.

If you will try to extend these both interfaces in the new interface you have to make choice of two, because Java forces you to resolve this collision explicitly.

First, you can declare method foo with the same signature as abstract, which will override A and B behaviour.

public interface ABExtendsAbstract extends A, B {
    @Override
    void foo();
}

And when you will implement ABExtendsAbstract in the class you will have to provide foo implementation:

public class ABExtendsAbstractImpl implements ABExtendsAbstract {
    @Override
    public void foo() { System.out.println("ABImpl.foo"); }
}

Or second, you can provide a completely new default implementation. You also may reuse code of A and B foo methods by Accessing overridden default methods from implementing class.

public interface ABExtends extends A, B {
    @Override
    default void foo() { System.out.println("ABExtends.foo"); }
}

And when you will implement ABExtends in the class you will not have to provide foo implementation:

public class ABExtendsImpl implements ABExtends {}

Syntax:

  • public default void methodName() {/* method body */}

Contributors

Topic Id: 113

Example Ids: 454,456,2442,4010,4101,29662

This site is not affiliated with any of the contributors.