Skip to content
This repository was archived by the owner on Aug 27, 2026. It is now read-only.
This repository was archived by the owner on Aug 27, 2026. It is now read-only.

Java 8 Interface Binding #25

Description

@jonpryor

Android N uses new Java 8 language features such as interface default methods and interface static methods, which are features that tools/generator hasn't had to deal with before.

The question: how should they be dealt with?

    // Java
    public interface HelloJava8 {
        public static final int VALUE = 42;
        public void a ();
        public default void defaultMethod() {
        }
        public static void staticMethod() {
        }
    }

There are at least three ways to do so:

  1. Ignore them.
  2. Expose them.
  3. Treat them specially.

Ignore them

Interface default methods don't need to be implemented, and static methods aren't implementable. The binding mechanism could thus simply ignore them entirely.

    // C#
    public interface IHelloJava8 {
        public void A ();
    }
    public static class HelloJava8 {
        public const int Value = 42;
    }

This isn't entirely desirable, because it also means default interface methods can't be called.

    IHelloJava8 value = ...
    value.DefaultMethod(); // error
    // No way to invoke HelloJava8.staticMethod()

On the plus side, implementors don't need to worry about them:

    public class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
    }

Expose them

The "simple" version of "expose them" is do the simple thing: static methods are bound in the constant-containing static class, and default methods are bound as normal methods:

    // C#
    public interface IHelloJava8 {
        public void A ();
        public void DefaultMethod ();
    }
    public static partial class HelloJava8 {
        public const int Value = 42;
        public void StaticMethod ()
        {...}
    }

This allows calling both methods:

    IHelloJava8 value = ...
    value.DefaultMethod(); // works
    HelloJava8.StaticMethod(); // works

However, this means that C# subclasses need to implement all the default methods:

    public class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
        public void DefaultMethod () {...}
    }

This might not seem bad in this example, but java.util.Collection contains five default methods (one inherited from Iterable).

This might be fine and acceptable, and we could help this scenario by providing access to the default method implementation:

    public static partial class HelloJava8 {
        public void InvokeDefaultMethod(IHelloJava8 self)
        {
            /* Non-virtual invocation of HelloJava8.defaultMethod() */
        }
    }

    public partial class WootJava8 {
        public void DefaultMethod ()
        {
            HelloJava8.InvokeDefaultMethod (this);
        }
    }

However, such "help" will require documentation and training to know about, complicating use.

Treat them specially

To a large extent, the concern is around C# implementations of the interface. It is seen that requiring that optional methods be implemented will complicate the C# experience. Perhaps we can fix this by splitting out the optional methods?

Then, we can use extension methods to invoke the default methods:

    // C#
    public interface IHelloJava8 {
        public void A ();
    }
    public static partial class HelloJava8 {
        public const int Value = 42;
        public void StaticMethod ()
        {...}

        public void DefaultMethod (this IHelloJava8 self)
        {
            // *Virtual* dispatch to invoke self.defaultMethod()
        }

        public interface IDefaultMethods {
            public void DefaultMethod ();
        }
    }

This allows calling both methods:

    IHelloJava8 value = ...
    value.DefaultMethod(); // works via extension method
    HelloJava8.StaticMethod(); // works

It also means that C# types don't need to implement the default methods:

    public partial class WootJava8 : Java.Lang.Object, IHelloJava8 {
        public void A () {}
    }

...but if the C# type does want to implement them, they can all be implemented by using the IDefaultMethods interface:

    public partial class WootJava8 : HelloJava8.IDefaultMethods {
        public void DefaultMethod () {}
    }

Activity

  1. jonpryor commented on Apr 29, 2016

    @jonpryor
    ContributorAuthor

    At present, I'm leaning toward (2) Expose Them, if only because it's implementation is simple (though the documentation effort regarding the presence of HelloJava8.InvokeDefaultMethod() isn't simple).

  2. mtilbrook-dev commented on Apr 30, 2016

    @mtilbrook-dev

    I don't think ignoring them is an option.
    I don't like (2) Expose Them. It adds unneeded name mangling. The documentation issue is also a major challenge.
    I like (3) Treat them specially, It feels much cleaner from a consumer perspective.

  3. jonpryor commented on May 1, 2016

    @jonpryor
    ContributorAuthor

    I don't like (2) Expose Them. It adds unneeded name mangling.

    I don't understand what you mean by "unneeded name mangling." What name mangling? The Invoke helpers?

    The documentation issue is also a major challenge.

    Agreed. However, (3) also has documentation issues, just different documentation issues.

  4. mtilbrook-dev commented on May 2, 2016

    @mtilbrook-dev

    Yes, the Invoke helper. Mangling might not have been the right language there.

    What documentation do you think will be needed? I can understand that (2) Invoke Requiring docs

  5. jonpryor commented on May 2, 2016

    @jonpryor
    ContributorAuthor

    What documentation do you think will be needed [in (3)]?

    The presence and use of the IDefaultMethods interface. It'll be a situation of "Java declares a HelloJava8.defaultMethod() interface method, and I can call that method from C#, but how do I implement that method in C#? It's not present in the IHelloJava8 interface." It's in the HelloJava8.IDefaultMethods interface, which code completion won't know about, and who's going to know to look for that interface without...documentation. :-)

    (2) answers that question: it's an interface method, therefore you implement it. It's only when you don't want to implement the default interface method that documentation is required, or when you want to invoke the default behavior.

    Both (2) and (3) raise the question of what to do with our "missing abstract method generator" (@radekdoulik? ;-) -- presumably it should check for and use the default interface method if present, but we'd need a way for the generator to know that (likely a new custom attribute).

  6. jonpryor commented on Jun 24, 2016

    @jonpryor
    ContributorAuthor

    Ignore them and Treat them specially don't interact well with Java versioning: default interface methods aren't restricted to new methods; existing methods can be made default!

    Case in point: [java.lang.reflect.AnnotatedElement.html.isAnnotationPresent()](https://developer.android.com/reference/java/lang/reflect/AnnotatedElement.html#isAnnotationPresent%28java.lang.Class<? extends java.lang.annotation.Annotation>%29) is non-default in API-23 and is now default in API-24. The Ignore them and Treat them specially approaches result in removing the IsAnnotationPresent() method from the IAnnotatedElement interface, which would break all implementations.

    This can be fixed in Mono.Android.dll -- we know what the previous API is, we can manually fixup such removed members -- but I'm dubious of the effects on 3rd party .jar bindings such as the Android Support library. I believe that most .jar bindings will consist of as little effort as possible to get a compiling binding, and if the .jar changes interface methods to default interface methods between versions, this can break existing C# interface implementations.

    I'm very dubious of solutions (1) and (3) right now.

  7. jonpryor commented on Jun 24, 2016

    @jonpryor
    ContributorAuthor

    The Expose them (2) approach has a different problem: if default methods are added to an interface over time, that means that previously built C# classes implementing the interface will break.

    Xamarin.Android 6.1 added some support to "fixup" added interface methods with an implementation that throws AbstractMethodError, which made sense then, but doesn't make sense in the context of default interface methods.

    What we can instead do is place a custom attribute on the default method which refers to the class with the Invoke method wrapper:

    public interface IHelloJava8 {
        public void A ();
        [JavaDefaultInterfaceMethod (typeof (HelloJava8))]
        public void DefaultMethod ();
    }
    partial public class HelloJava8 {
        public void InvokeDefaultMethod(IHelloJava8 self) {...}
    }
    

    The abstract method fixup code could then fixup "missing" interface methods to invoke the default method:

    /* IHelloJava8 vPrev lacks defaultMethod(), for arguments sake */
    class MyHello : IHelloJava8 {
        public void A () {}
        // NO implementation of DefaultMethod()
    }
    

    During Xamarin.Android packaging, we would fix MyHello to be:

    class MyHello : IHelloJava8 {
        public void A () {}
        public void DefaultMethod()
        {
            HelloJava8.InvokeDefaultMethod (this);
        }
    }
    

    This would extend the existing support for versioned interfaces in a natural manner, causing the generated method implementation to invoke the default method instead of throwing an exception.

  8. atsushieno commented on Jul 8, 2016

    @atsushieno
    Contributor

    While all of those choices have problems, their severity on the issues are different.

    I believe 1) is the only choice we can take with least problem so far (and that's what we do right now). It is the most conservative and the safest on future API breakage than other choices. By choosing 2) or 3) you would have already brought troubles regarding IAudioRouting fixes.
    dotnet/android@576fb98

    Default interface methods that "used to be non-default" can be "fixed" by binding authors to change "abstract" attribute on the method i.e.:

    <attr path="/path/to/the/method" name="abstract">true</attr>
    

    It should be also noted that those binding authors should be responsible to API changes. They can be the only responsible party.

    1. Apart from the known obvious issue, IAudioRouting.OnRoutingChangedListener also exposed another issue: it is possible for Java8 to add a new base interfaces to an existing interface without API breakage when such new base interfaces only have default methods. If you blindly bind those default methods in C#, it (of course) results in API breakage (i.e. missing method implementation) to any implementor of the interface.

    It should be noted that once we expose extraneous methods to implement, especially at generator level, and we find any fatal problem (like we did for IAudioRouting), we cannot go back to the original state because it would already bring API breakage to everyone. It is the worst scenario.

    1. is almost 1), plus that there is slight chance for some problem that extension method could bring future compatibility issue regarding member resolution, especially because it resides in the same namespace as the primary interface.

    Having said that 3) is worth implementing and probably even providing as an opt-in feature.

  9. ddobrev commented on Oct 12, 2018

    @ddobrev

    Please fix because we cannot use the latest ExoPlayer.

  10. atsushieno commented on Oct 13, 2018

    @atsushieno
    Contributor

    You should report specific, individual, actual issues as a new issue, not here without details.

    The support for default interface methods is implemented at #341 (when I was at Xamarin)

  11. brendanzagaeski commented on Sep 25, 2019

    @brendanzagaeski
    Contributor

    Release status update

    A new Preview version of Xamarin.Android has now been published on Windows that includes the new capabilities related to this item. See the release notes for additional details.

    The new functionality is not yet available on macOS. I will update this item again when a Preview version with the new functionality is available on macOS.

    Feature included in Xamarin.Android 10.0.99.100.

    Feature included on Windows in Visual Studio 2019 version 16.4 Preview 1. To try the Preview version that includes the fix, check for the latest updates in Visual Studio Preview.

    Feature not yet available on macOS.

  12. brendanzagaeski commented on Dec 4, 2019

    @brendanzagaeski
    Contributor

    Release status update

    A new Release version of Xamarin.Android has now been published on Windows that includes the new capabilities related to this item. See the release notes for additional details.

    The new functionality is not yet published in a Release version on macOS. I will update this item again when a Release version with the new functionality is available on macOS.

    Feature included in Xamarin.Android 10.1.0.30.

    Feature included on Windows in Visual Studio 2019 version 16.4. To get the new version that includes the feature, check for the latest updates or install the latest version from https://visualstudio.microsoft.com/downloads/.

    (Feature also included on macOS in Visual Studio 2019 for Mac version 8.4 Preview 2.1 and higher. To try the Preview version that includes the feature, check for the latest updates on the Preview updater channel.)

  13. brendanzagaeski commented on Jan 8, 2020

    @brendanzagaeski
    Contributor

    Release status update

    A new Release version of Xamarin.Android has now been published on macOS that includes the new capabilities related to this item. See the release notes for additional details.

    Feature included in Xamarin.Android 10.1.1.0.

    Feature included on macOS in Visual Studio 2019 for Mac version 8.4. To get the new version that includes the feature, check for the latest updates on the Stable updater channel.

  14. locked and limited conversation to collaborators on Apr 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions