Java 8 “default method” versus C# “extend method”

Question:

Java 8 introduces the concept of "standard method" to allow you to add new functionality to an interface.

C# provides "extension methods" that allow you to "add" methods (features) to existing types.

Taking into account how they are implemented by the language and possible usage scenarios, in what respects are they equivalent and in which do they differ?

Answer:

The C# extension method alone is not equivalent to the standard Java method. Only when the interface is added can the mechanism be comparable.

It is immediately clear that in Java the syntax is more convenient.

standard method

The standard Java method becomes part of the type. There is an inheritance from the interface method to the class method. Here are the methods, the class passes to have them inside them. Any kind of access to the class, even by reflection, will indicate their presence.

In this case, if the programmer doesn't want the default implementation, he can write his own to serve that interface. If a class inherits from this class, the implementation of the inherited one can write another implementation without problems.

The method is virtual, as every method is interfaced. It is a runtime mechanism according to the interface/class declaration. It has the advantages and disadvantages of that. The VM needs to handle the health.

extension method

Even though it's on top of an interface and even though it looks like a type that implements the interface has that method, the extension method is still static and totally untyped. As they are static, these methods work more like functions.

As the extension method has lower priority, in general this same effect will be obtained if the class implements the method declared in the interface, at any point in the hierarchy, but not in a polymorphic way. It will only work if it is called by the specific type and the compiler knows what that type is. If the decision is to be dynamic, it will be made static and will call the static extension method. It may end up calling what it shouldn't or at least what the programmer doesn't expect.

I cannot guarantee (I don't have enough knowledge) that there are no extra specific cases where this could be a problem. One obvious one is that it's easier to have name clashes. Another is that it can give rise to dangerous tricks.

Since the method is external to the type, it cannot be accessed directly by the type, it is independent and isolated.

This is a language feature and fully resolved by the compiler as the method is used (as opposed to its declaration). The CLR doesn't even need to know how to deal with this.

An advantage of extension methods (some people say it's a disadvantage) is that you can add new methods to an interface and make all classes that purport to implement the interface already have the method. With the Java engine you have to modify the interface to add the new method.

Now C# has the same mechanism for doing something more similar to Java. There are more ambitious proposals that propose other mechanisms, such as traits , but they should not prosper.

I tried to do the same example in Java and C# according to what each language provides:

Java

public class HelloWorld {
    public static void main(String args[]) {
        Veiculo veiculoT = new Veiculo();
        System.out.println("veiculoT");
        veiculoT.diagnostico();
        veiculoT.liga();
        Testador.teste(veiculoT);
        Financeiro.compra(veiculoT);
        System.out.println("---------------");
        Veiculo carro = new Carro();
        System.out.println("carro");
        carro.diagnostico();
        carro.liga();
        Testador.teste(carro);
        Financeiro.compra(carro);
        System.out.println("---------------");
        Carro carro2 = new Carro();
        System.out.println("carro2");
        carro2.diagnostico();
        carro2.liga();
        carro2.buzina();
        Testador.teste(carro2);
        Financeiro.compra(carro2);
    }
}

interface Funcionamento {
    default void liga() {
        System.out.println("Ligou!");
    }
    static void buzina() {
        System.out.println("Buzinou!");
    }
}

class Testador {
    public static void teste(Funcionamento veiculo) {
        System.out.println("Vai testar");
        veiculo.liga();
    }
}

class Financeiro {
    public static void compra(Veiculo veiculo) {
        System.out.println("Comprou N: " + veiculo.id);
        veiculo.diagnostico();
        veiculo.liga();
    }
}

class Veiculo implements Funcionamento {
    public int id = 1;
    public void diagnostico() {
        Funcionamento.super.liga();
        Funcionamento.buzina();
        liga();
        System.out.println("Tudo ok!");
    }
}

class Carro extends Veiculo {
    public void liga() {
        System.out.println("Vrom!");
    }
    public void buzina() {
        System.out.println("bi bi!");
    }
}

See working on ideone . And on repl.it. Also posted on GitHub for future reference .

Ç#

using static System.Console;

public class HelloWorld {
    public static void Main() {
        Veiculo veiculoT = new Veiculo();
        WriteLine("veiculoT");
        veiculoT.diagnostico();
        veiculoT.liga();
        veiculoT.teste();
        veiculoT.compra();
        WriteLine("---------------");
        Veiculo carro = new Carro();
        WriteLine("carro");
        carro.diagnostico();
        carro.liga();
        carro.teste();
        carro.compra();
        WriteLine("---------------");
        Carro carro2 = new Carro();
        WriteLine("carro2");
        carro2.diagnostico();
        carro2.liga();
        carro2.buzina();
        carro2.teste();
        carro2.compra();
    }
}

interface IFuncionamento {
    void liga();
}

static class Funcionamento {
    public static void liga(this IFuncionamento func) => WriteLine("Ligou!");
    public static void buzina() => WriteLine("Buzinou!");
}

static class Testador {
    public static void teste(this IFuncionamento veiculo) {
        WriteLine("Vai testar");
        veiculo.liga();
    }
}

static class Financeiro {
    public static void compra(this Veiculo veiculo) {
        WriteLine("Comprou N: " + veiculo.id);
        veiculo.diagnostico();
        veiculo.liga();
    }
}

class Veiculo : IFuncionamento {
    public int id = 1;
    public void diagnostico() {
        Funcionamento.liga(this);
        Funcionamento.buzina();
        liga();
        WriteLine("Tudo ok!");
    }
    public virtual void liga() => Funcionamento.liga(this);
}

class Carro : Veiculo {
    public override void liga() => WriteLine("Vrom!");
    public void buzina() => WriteLine("bi bi!");
}

See working on ideone . And in .NET Fiddle . Also posted on GitHub for future reference .

Note that methods external to the type such as Teste() and Compra() can be called more conveniently in C#. But the Liga() method needs an extra class with extension method to contain the implementation. It also needs to have an implementation in the concrete class, even if it only delegates to the extension method, without that there is no polymorphism.

In C# this is an error-inducing trick and makes it difficult to debug and use reflection, and can yield unexpected results for the most naive programmer.

Scroll to Top