Skip to content

Covariant Return Types (Class)

Jonathan Pobst edited this page Mar 23, 2021 · 2 revisions

Imagine the following Java code:

public abstract class Animal
{
    abstract Animal get ();
}

public class Dog : Animal
{
    @Override
    public Dog get ()
    {
        return new Dog ();
    }
}

The Dog.get () method implements the abstract Animal.get () as required by the abstract Animal class, however it changes the return type from Animal to Dog. Changing the return type to a more specific type (Dog inherits Animal) is called a covariant return type. This is allowed in Java, however it is historically not allowed in C#. (Xamarin in .NET6 will support C# 9, which supports many -but not all- cases of covariant return types.)

This scenario is overriding an abstract class method. A similar scenario is implementing an interface: Covariant Return Types (Interface).

The above example generates the following C# code:

// Metadata.xml XPath class reference: path="/api/package[@name='my.package']/class[@name='Animal']"
[global::Android.Runtime.Register ("my/package/Dog", DoNotGenerateAcw=true)]
public abstract partial class Animal : Java.Lang.Object
{
    // Metadata.xml XPath method reference: path="/api/package[@name='my.package']/class[@name='Animal']/method[@name='get']"
    [Register ("get", "()Lmy/package/Animal;", "GetGetHandler")]
    public abstract unsafe global::My.Package.Animal Get ();
}

// Metadata.xml XPath class reference: path="/api/package[@name='my.package']/class[@name='Dog']"
[global::Android.Runtime.Register ("my/package/Dog", DoNotGenerateAcw=true)]
public partial class Dog : global::My.Package.Dog
{
    // Metadata.xml XPath method reference: path="/api/package[@name='my.package']/class[@name='Dog']/method[@name='get']"
    [Register ("get", "()Lmy/package/Dog;", "GetGetHandler")]
    public virtual unsafe global::My.Package.Dog Get ()
    {
	...
    }
}

That is, looking at the generated code, you can see that the Get method was generated, however it does not have the same return type as the abstract Animal.Get () method, and thus:

  • The Java-to-C# code generator did not mark Dog.Get () as an override of Animal.Get ()
  • The C# compiler does not think the Dog class implements the abstract method Animal.Get ()

Compiling this code causes a C# error:

Error CS0534 'My.Package.Dog' does not implement inherited abstract member 'My.Package.Animal.Get()'

In order to fix this, we need to instruct the Java-to-C# code generator to use My.Package.Animal as a return type instead of My.Package.Dog. This can be done with the managedReturn type of metadata, which changes the "managed" (C#) method "return" type. Using the XPath specified in the generated method above, the metadata would look like this:

<attr path="/api/package[@name='my.package']/class[@name='Dog']/method[@name='get']" name="managedReturn">My.Package.Animal</attr>

With this metadata in place, the following C# method is generated, which satisfies the Animal abstract contract:

// Metadata.xml XPath method reference: path="/api/package[@name='my.package']/class[@name='Dog']/method[@name='get']"
[Register ("clone", "()Lmy/package/Animal;", "GetCloneHandler")]
public virtual unsafe global::My.Package.Animal Get ()
{
    ...
}